Кажется, что время — это река, которую внезапно переклинило, и она решила течь по кругу. Именно такое впечатление складывается на первый взгляд, когда видишь, что вновь стали популярны боты в мессенджерах. Но это впечатление обманчиво. Изменилось очень многое — мощности, которые стоят за ботами, возможность обработки ими мультимедиа информации, наличие информации о пользователях, круг охвата… В общем, это явно не ностальгический тренд, а реально полезная технология, которая будет развиваться и дальше.
Боты становятся всё сложнее, они берут на себя многие функции других каналов. Например, вместо того, чтобы звонить по телефону и слушать по полчаса записанную девушку, которая говорит тебе перейти в тоновый режим и набрать волшебную последовательность символов, всё то же самое можно сделать при помощи бота. И это будет быстрее, удобнее, гибче и дешевле.
Для некоего личного проекта мне захотелось написать бота с довольно сложной ветвящейся логикой (например, это может быть система поддержки или диагностики с глубокой вложенностью). При этом граф данной логики имеет огромное количество разветвлений. В общем, быстро стало очевидно, что без автоматизированного тестирования не обойтись — иначе что-то точно упущу из внимания. И насколько же сильно я удивился, когда узнал, что способа тестировать логику ботов просто нет!
Конечно, можно зарегистрировать дополнительного бота для тестирования, но это вариант кривой и некрасивый. Обращение ко внешнему апи во время тестов, заглушка, которая не даст общаться с ботом кому попало, ограничение на скорость отправки сообщений раз в секунду… Если слать сообщение раз в секунду, то граф из каких-то 60 вершин будет тестироваться уже больше минуты! И я уже не говорю о том, что у нас нет никакой возможности смоделировать возросшую нагрузку на бота, при которой он упрётся в ограничение в 30 сообщений в секунду… В общем, я понял, что опять придётся делать что-то своё.
Первый вариант с огромной скоростью реализации — это просто надстройка над самой популярной Node.JS библиотекой для реализации Telegram ботов — node-telegram-bot-api. Сделано довольно просто — переписывается обработчик события на отсылку данных, после чего мы обрабатываем данные своим методом и посылаем вручную сгенерированный запрос. Выходит примерно так:
Бота, который используется для данного примера, вы можете найти на страничке библиотеки, как и собственно этот пример — не хочется приводить лишнего копи-пейста.
Решение выше хорошо для быстрого тестирования каких-то простых вещей и покроет большинство потребностей тестирования. Однако что делать, если мы хотим, скажем, тестировать, как наш бот работает под большой нагрузкой? В этом случае единственный логичный выход — это реализовать свой вариант Telegram API. Звучит довольно страшно, но на самом деле нам нужна простая реализация, которая совместима с текущими библиотеками для ботов и позволяет отправлять клиентские запросы. Кстати, как побочная фича — получается, что мы делаем полноценный сервер, с которым можно работать из любого другого стека технологий — хоть питон, хоть C#, хоть что.
To make the long story short, я сделал и такой вариант. С ним тестирование логики того же бота выглядит примерно так:
То есть, мы поднимаем у себя на локальном порту сервер, дальше стандартным образом настраиваем бота на работу с этим сервером, и просто шлём ему сообщения от ботов и клиентов. Объект клиента можно получить прямо из сервера, или можно написать своего клиента — сервер просто принимает сообщения в стандартном JSON формате по определённому адресу.
Конечно, осталось огромное количество всяких полезных вещей, которые можно добавить. К примеру, сейчас очередь сообщений хранится просто в массиве в оперативной памяти. Не реализовано эмулирование таймаутов, и поддерживается работа только с одним клиентом одновременно (сервер просто высылает все сообщения клиенту, который обратился к нему за данными от определённого бота, идентифицируемого по токену). Так же есть поддержка только отправки текстовых сообщений. Связано это с тем, что меня на текущий момент интересует только текст.
Первому проекту особо добавить нечего, а второй скорее всего будет развиваться в сторону моих личных потребностей. Но я немного рассчитываю на поддержку open source сообщества. Лицензия MIT, поддержка Node.js 4 и 6 (5 тоже будет работать, но на ней не пройдут тесты, поскольку используемый для теста бот несовместим с Node 5. Впрочем, я уже создал на это pull request), репозитории ждут ваших пулл реквестов с кодом, оформленным по включённой в проект конфигурации линтера. Некое количество тестов тоже есть. Возможно, со временем будет поддержка ботов и для других мессенджеров.
Если вам интересна сама тема ботов, то оставайтесь на связи. Будет интересно.
Боты становятся всё сложнее, они берут на себя многие функции других каналов. Например, вместо того, чтобы звонить по телефону и слушать по полчаса записанную девушку, которая говорит тебе перейти в тоновый режим и набрать волшебную последовательность символов, всё то же самое можно сделать при помощи бота. И это будет быстрее, удобнее, гибче и дешевле.
Для некоего личного проекта мне захотелось написать бота с довольно сложной ветвящейся логикой (например, это может быть система поддержки или диагностики с глубокой вложенностью). При этом граф данной логики имеет огромное количество разветвлений. В общем, быстро стало очевидно, что без автоматизированного тестирования не обойтись — иначе что-то точно упущу из внимания. И насколько же сильно я удивился, когда узнал, что способа тестировать логику ботов просто нет!
Конечно, можно зарегистрировать дополнительного бота для тестирования, но это вариант кривой и некрасивый. Обращение ко внешнему апи во время тестов, заглушка, которая не даст общаться с ботом кому попало, ограничение на скорость отправки сообщений раз в секунду… Если слать сообщение раз в секунду, то граф из каких-то 60 вершин будет тестироваться уже больше минуты! И я уже не говорю о том, что у нас нет никакой возможности смоделировать возросшую нагрузку на бота, при которой он упрётся в ограничение в 30 сообщений в секунду… В общем, я понял, что опять придётся делать что-то своё.
Два решения задачи
Надстройка
Первый вариант с огромной скоростью реализации — это просто надстройка над самой популярной Node.JS библиотекой для реализации Telegram ботов — node-telegram-bot-api. Сделано довольно просто — переписывается обработчик события на отсылку данных, после чего мы обрабатываем данные своим методом и посылаем вручную сгенерированный запрос. Выходит примерно так:
describe('Telegram Test', ()=> {
const myBot = new TestBot(telegramBot);
let testChat = 0;
it('should greet Masha', () => {
const telegramTest = new TelegramTest(telegramBot);
testChat++;
return telegramTest.sendUpdate(testChat, '/ping')
.then((data)=> {
if (data.text === 'pong') {
return telegramTest.sendUpdate(testChat, '/start');
}
throw new Error(`Wrong answer for ping! (was ${data.text})`);
})
.then(data=> telegramTest.sendUpdate(testChat, data.keyboard[0][0].text))
.then((data)=> {
if (data.text === 'Hello, Masha!') {
return true;
}
throw new Error('Wrong greeting!');
});
});
});
Бота, который используется для данного примера, вы можете найти на страничке библиотеки, как и собственно этот пример — не хочется приводить лишнего копи-пейста.
Свой сервер API Telegram
Решение выше хорошо для быстрого тестирования каких-то простых вещей и покроет большинство потребностей тестирования. Однако что делать, если мы хотим, скажем, тестировать, как наш бот работает под большой нагрузкой? В этом случае единственный логичный выход — это реализовать свой вариант Telegram API. Звучит довольно страшно, но на самом деле нам нужна простая реализация, которая совместима с текущими библиотеками для ботов и позволяет отправлять клиентские запросы. Кстати, как побочная фича — получается, что мы делаем полноценный сервер, с которым можно работать из любого другого стека технологий — хоть питон, хоть C#, хоть что.
To make the long story short, я сделал и такой вариант. С ним тестирование логики того же бота выглядит примерно так:
it('should greet Masha', function testFull() {
this.slow(400);
this.timeout(800);
let serverConfig = {port: 9000};
let server = new TelegramServer(serverConfig);
let token = 'sampleToken';
let client = server.getClient(token);
let message = client.makeMessage('/start');
let telegramBot,
testBot;
return server.start()
.then(()=> client.sendMessage(message))
.then(()=> {
let botOptions = {polling: true, baseApiUrl: server.ApiURL};
telegramBot = new TelegramBot(token, botOptions);
testBot = new TestBot(telegramBot);
return client.getUpdates();
})
.then((updates)=> {
console.log(colors.blue(`Client received messages: ${JSON.stringify(updates.result)}`));
if (updates.result.length !== 1) {
throw new Error('updates queue should contain one message!');
}
let keyboard = JSON.parse(updates.result[0].message.reply_markup).keyboard;
message = client.makeMessage(keyboard[0][0].text);
client.sendMessage(message);
return client.getUpdates();
})
.then((updates)=> {
console.log(colors.blue(`Client received messages: ${JSON.stringify(updates.result)}`));
if (updates.result.length !== 1) {
throw new Error('updates queue should contain one message!');
}
if (updates.result[0].message.text !== 'Hello, Masha!') {
throw new Error('Wrong greeting message!');
}
return true;
})
});
То есть, мы поднимаем у себя на локальном порту сервер, дальше стандартным образом настраиваем бота на работу с этим сервером, и просто шлём ему сообщения от ботов и клиентов. Объект клиента можно получить прямо из сервера, или можно написать своего клиента — сервер просто принимает сообщения в стандартном JSON формате по определённому адресу.
TODO
Конечно, осталось огромное количество всяких полезных вещей, которые можно добавить. К примеру, сейчас очередь сообщений хранится просто в массиве в оперативной памяти. Не реализовано эмулирование таймаутов, и поддерживается работа только с одним клиентом одновременно (сервер просто высылает все сообщения клиенту, который обратился к нему за данными от определённого бота, идентифицируемого по токену). Так же есть поддержка только отправки текстовых сообщений. Связано это с тем, что меня на текущий момент интересует только текст.
Первому проекту особо добавить нечего, а второй скорее всего будет развиваться в сторону моих личных потребностей. Но я немного рассчитываю на поддержку open source сообщества. Лицензия MIT, поддержка Node.js 4 и 6 (5 тоже будет работать, но на ней не пройдут тесты, поскольку используемый для теста бот несовместим с Node 5. Впрочем, я уже создал на это pull request), репозитории ждут ваших пулл реквестов с кодом, оформленным по включённой в проект конфигурации линтера. Некое количество тестов тоже есть. Возможно, со временем будет поддержка ботов и для других мессенджеров.
Если вам интересна сама тема ботов, то оставайтесь на связи. Будет интересно.
Ссылки
- Первая реализация (надстройка над node-telegram-bot-api);
- Вторая реализация (эмуляция Telegram API);
- Хорошая статья про борьбу с ограничениями на скорость отправки сообщений на go;
- Официальные ограничения на скорость отправки сообщений.
Поделиться с друзьями
stim644
А если просто отделить модель и тестировать только модель?
Т.е. будет класс Телеграм и класс модель:
Telegram {
Model model;
sendComand1(String data) { model.sendComand1(data);}
}
jehy
Конечно, можно. Но чем больше мы уходим от конкретной реализации, тем больше появляется шансов облажаться.
В первом случае преимуществом будет то, что всё проходит через API настоящего бота, и у вас точно не возникнет проблем совместимости с ним, а во втором вы дополнительно можете пользоваться всеми преимуществами тестового сервера — от использования другого языка разработки до эмуляции ошибок и таймаутов, что в случае тестирования через модель уже не сделать.