-
Notifications
You must be signed in to change notification settings - Fork 21
Testing
Charlie has pretty extensive unit-ish tests. These tests are built with Jest as a test runner as well as all of its built-in expectations and mocking functionality.
In addition to Jest and everything it has built-in, Charlie has some testing
utilities of its own. For starters, global fetch
, the brain,
and all of the internal APIs are mocked. To use mocked
utilities in your tests, just require them before requiring the module to be
tested:
// Imported a mocked brain to use for testing.
const {
brain,
utils: { slack, tock },
} = require("../test");
The mocks provided here are all Jest mocks, so they have all the APIs and behaviors as any other Jest mock. It is recommended that part of your test include resetting all mocks at the beginning of each test to cut down on the risk of cross-test pollution:
beforeEach(() => {
jest.resetAllMocks();
});
There is also a helper function called getApp()
which will create an object
that looks like a very simple Bolt app object where the methods of the app are
mocked so you can control their behavior or test how they are used
The app mock handles providing a brain mock, mocks for subscribing to Bolt
events (action
, event
, message
), and Bolt's logger.
Since the bot will subscribe to events on the mock app, it also provides some
utility functions for retrieving the listeners. These utility functions are
directly on the mock app
object.
-
getHandler(Number index) : Function | null
Get a
message
handler that the bot registered with the mock app; e.g., with:module.exports = (app) => { app.message("hello world", () => { // handler here... }); };
index
defaults to zero, or for bots that register multiplemessage
handlers, you can provide the index to specify which one you want. If there is no handler at the provided index, returnsnull
-
getActionHandler(Number index) : Function | null
Same as above, but action handlers. E.g., those registered with
app.action()
. -
getEventHandler(Number index) : Function | null
Same again, but event handlers, registered with
app.event()
.
An example usage:
const { getApp } = require("../utils");
const module = require("./awesomebot");
const app = getApp(); // Get the mocked app...
const awesomebot = module(app); // and pass it into the bot for initialization
// Assert that the bot subscribed to messages
expect(app.message).toHaveBeenCalledWith("awesome", expect.any(Function));
// Get the message handler the bot provided when it subscribed
const handler = app.getHandler();
const say = jest.fn();
// Call the handler
handler({ say });
// And make sure it did what we expected
expect(say).toHaveBeenCalledWith("YOU ARE AWESOME!");
-
To mock a 3rd-party module, first require it into your test, then use
jest.mock()
on it, and only then require the module under test. If you require the module under test before mocking, it will get the "real" dependency instead of the mocked one. -
For bots that rely on time, check out Jest's fake timers. Charlie does not use the "legacy fake timers."
-
If a bot does any initialization outside of the main export, use the jest.resetModules() utility to reload the module so you can test and control initialization behavior.
This should probably be considered a warning sign. There is no good reason bots should initialize before their main method is executed, generally. Many of the ones that do so now are legacy
Charlie developer documentation