botfactory-e2e is an independent testing framework for adamant-botfactory that uses Fluent interface.
To use this framework, you would need to install botfactory-e2e
via your favorite package manager, e.g. npm
:
npm install botfactory-e2e
For a better experience, you may also want to install another testing framework. This documentation uses Jest
in its examples, but you can use any other testing framework you prefer.
Let's get started by writing a test for a hypothetical bot that greetings a user. First, create a bot.js
file:
import {createBot} from 'adamant-botfactory';
const bot = createBot(process.env.PASSPHRASE, {
nodes: [
/* ... */
],
});
bot.command('start', usr => usr.reply(`Hello, ${usr.address}!`));
export {bot};
As you can see, we don't start the bot in the same file as we create it because it would interfere with our tests. You might want to create a separate file named start.js
to run the bot.
Then, create a file named bot.test.js
. This will contain our actual test:
import {createTestBot, createMockNode} from 'botfactory-e2e';
import {bot} from './bot.js';
const node = createMockNode('my-test-node');
describe('Greeting Bot', () => {
const testBot = createTestBot(bot, {node});
it('should greet a user with the correct address', done => {
const passphrase =
'angry special raise embark animal you ball million bronze science crater review';
const user = createFakeUser(passphrase);
user
.interactsWith(testBot)
.sends({command: 'start'})
.shouldReceive(({message}) =>
expect(message.text).toBe('Hello, U14581577999389437648!')
)
.then(done);
});
});
afterAll(() => {
node.shutdown();
});
There's much going on, so let's break this test down line by line. Firstly, we need to create a fake node:
const node = createMockNode('my-test-node');
This will start a local server to fake an ADAMANT node with mock responses. We must pass a unique id for the fake node to separate it from others and allow for future reuse.
Next, we create a test bot:
const testBot = createTestBot(bot, {node});
What it does is simply make a copy of the bot with a mock passphrase and connect it to the fake node we created earlier.
Finally, we can create an interface to test the bot using mock user:
const user = createFakeUser(passphrase);
Now, as we ready, we can tell the mock user which bot it should interact with:
user.interactsWith(testBot);
Then, we are going to test start
command, so we generate and send a mock message to the bot with the /start
command:
user.sends({command: 'start'});
To test the bot's response, we can use Jest's built-in assertion function inside the shouldReceive
method's callback. First argument of the callback is the next transaction our bot will send to the user:
user.shouldReceive(({message}) =>
expect(message.text).toBe('Hello, U14581577999389437648!')
);
As we finish the testing, we can let Jest know that we're done:
user.then(done);
You can use await
with the mock user instance as it's a thenable class:
await user.interactsWith(myBot);
await user.sends({message: 'Hello!'});
A mock node is a fake ADAMANT Node that doesn't perform any calculations but returns mock data, which you are free to modify.
When creating your first mock node, botfactory-e2e
will try to find a free port starting from 36668
. Once it finds a free port, it will create a single server for all the features of the mock nodes:
const node1 = createMockNode('first-node');
console.log(node1.url); // http://localhost:36668/first-node
const node2 = createMockNode('second-node');
console.log(node2.url); // http://localhost:36668/second-node
When creating a mock node, you must specify the fake node's ID, which you can use to clear the old node's data and reuse it:
let node;
beforeEach(() => {
node = createMockNode('test-node');
});
Then, you can pass the created nodes into createTestBot
:
const testBot = createTestBot(bot, {
nodes: [node1, node2],
});
// Or for a single node, you can use
const testBot = createTestBot(bot, {
node: node1,
});
Mock nodes can also be configured to include starting data, node information, and more.
Most of the properties can be set in the constructor of createMockNode
and edited using node.info()
:
const node = createMockNode('test-syncing-node', {
syncing: true,
blocks: 90,
consensus: 60,
});
// change the node status to synced
node.info({
syncing: false,
blocks: 0,
consensus: 100,
});
You can set whether the node is considered alive. If you set online
to false
, the fake node will return a 500
error status:
const myNode = createMockNode('my-node', {
online: false,
});
// get whether node is online
const status = myNode.online(); // false
// set node to online
myNode.online(true);
To test the bot's ability to find the fastest node, you can set the ping
property the same way you would with online
:
const myNode = createMockNode('my-node', {
ping: 5000, // 5 seconds
});
// get node's ping
const ping = myNode.ping(); // 5000
// set default ping
myNode.ping(60);
This will only affect /api/loader/status/ping
and /api/node/status
endpoints, as it's used by adamant-api-jsclient to determine the fastest node.
In addition to node information, you can also manage blocks, accounts, and transactions using mock nodes.
You can set the latest block using the setCurrentBlock
method, which will automatically set the height
and timestamp
of the block and connect it to the previous block:
const block = node.setCurrentBlock({
id: '11288856488163258860',
version: 0,
numberOfTransactions: 0,
// ...
totalForged: '20000000',
});
console.log(block.previousBlock); // 165438318875405182
If your bot depends on external account information, you can mock an account using the newAccount
method. This way, your bot will be able to retrieve information about the account, such as its balance:
import {createMockNode} from 'botfactory-e2e';
import {createKeypairFromPassphrase} from 'adamant-api';
const node = createMockNode('test-account-balance');
const {publicKey} = createKeypairFromPassphrase('apple banana...');
const myAccount = node.newAccount(publicKey);
myAccount.balance = {
balance: '100000',
unconfirmedBalance: '0',
};
// ...
const account = node.getAccount({address: myAccount.address});
console.log(`${account.address}: ${account.balance}`);
Test bot simply copies all the handlers from your bot and connects it with the fake nodes you provided, adding its keypair to the fake nodes data:
const testBot = createTestBot(bot, {
node,
passphrase:
'apple banana cherry date elderberry fig grape hazelnut iris juniper kiwi lemon',
});
const {publicKey} = node.getAccount(testBot.address);
// db299bb7fd288b387b0b94b539e2689c46f980ea7cfa0a53a26842f84f3c32bf
console.log(publicKey);
To interact with your bot from a user point of view, you can use fake user:
const keyPair = createKeypairFromPassphrase('my secret passphrase');
const user = createFakeUser(keyPair);
To mock the account data to the bot's node and set the bot to which the user will send messages, use interactsWith
method:
// bot.js
const bot = createBot('...', {
/* ... */
});
bot.command('start', async usr => {
usr.reply(JSON.stringify(await usr.balance()));
});
export {bot};
// bot.test.js
import {bot} from './bot.js';
// prepare local server to mock node and copy of the bot
const node = createMockNode('test-balance');
const testBot = createTestBot(bot, {node});
// construct the user
const keyPair = createKeypairFromPassphrase('apple banana...');
const user = createFakeUser(keyPair);
user
.interactsWith(testBot)
.sends({command: 'start'})
.shouldReceive(({message}) =>
expect(JSON.parse(message)).toMatchObject({
balance: '0',
unconfirmedBalance: '0',
})
);