Automated bot testing for Telegram
It seems that time is a river that suddenly pereklinilo, and she decided to flow in a circle. It is this impression that appears at first glance when you see that bots in instant messengers have become popular again. But this impression is misleading. A lot has changed - the capacities behind the bots, the ability to process multimedia information, the availability of user information, the scope ... In general, this is clearly not a nostalgic trend, but a really useful technology that will continue to develop.Bots are becoming more complex, they take on many of the functions of other channels. For example, instead of calling on the phone and listening for half an hour to a recorded girl who tells you to switch to tone mode and type in a magic sequence of characters, you can do the same with the bot. And it will be faster, more convenient, more flexible and cheaper.
For some personal project, I wanted to write a bot with a rather complicated branching logic (for example, it could be a support or diagnostic system with deep nesting). Moreover, the graph of this logic has a huge number of branches. In general, it quickly became apparent that automated testing could not be dispensed with; otherwise, I would definitely miss something. And how much I was surprised when I learned that there is a way to test the logic of botsjust not !
Of course, you can register an additional bot for testing, but this is a variant of the curve and ugly. An appeal to the external api during the tests, a stub that will prevent anyone from communicating with the bot, a limit on the speed of sending messages once a second ... If you send a message once a second, a graph of some 60 vertices will be tested for more than a minute! And I'm not talking about the fact that we have no way to simulate the increased load on the bot, in which it runs into a limit of 30 messages per second ... In general, I realized that again I have to do something different.
Two solutions to the problem
Add-in
The first option with great speed of implementation is just an add-on over the most popular Node.JS library for implementing Telegram bots - node-telegram-bot-api . It was done quite simply - the event handler is copied to send data, after which we process the data using our own method and send a manually generated request. It turns out something like this:
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!');
});
});
});You can find the bot that is used for this example on the page of the library, just like this example itself - I don’t want to bring an extra copy-paste.
Own Telegram API Server
The solution above is good for quickly testing some simple things and will cover most testing needs. However, what if we want to, say, test how our bot works under heavy load? In this case, the only logical way out is to implement your version of the Telegram API. It sounds pretty scary, but in fact we need a simple implementation that is compatible with current libraries for bots and allows you to send client requests. By the way, as a side feature - it turns out that we are making a full-fledged server with which you can work from any other technology stack - at least python, at least C #, at least.
To make the long story short, I made such an option. With it, testing the logic of the same bot looks something like this:
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;
})
});That is, we raise the server on our local port, then configure the bot in a standard way to work with this server, and simply send messages from bots and clients to it. The client object can be obtained directly from the server, or you can write your client - the server simply receives messages in standard JSON format at a specific address.
Todo
Of course, there are a huge amount of all sorts of useful things that can be added. For example, now the message queue is stored simply in an array in RAM. Emulation of timeouts is not implemented, and only one client is supported at a time (the server simply sends all messages to the client who turned to him for data from a specific bot identified by token). There is also support only for sending text messages. This is due to the fact that at the moment I am only interested in the text.
The first project has nothing to add, and the second is likely to develop towards my personal needs. But I rely a little on the support of the open source community. MIT license, support for Node.js 4 and 6 (5 will also work, but it will not pass the tests, since the bot used for the test is incompatible with Node 5. However, I already created a pull request for this ), repositories are waiting for your pull requests from code issued according to the linter configuration included in the project. There are some tests too. Perhaps, over time, there will be support for bots for other messengers.
If you are interested in the theme of bots, then stay in touch. It will be interesting.
References
- First implementation (add-on over node-telegram-bot-api);
- The second implementation (Telegram API emulation);
- A good article on how to deal with restrictions on the speed of sending messages on go;
- Official restrictions on the speed of sending messages.