Testing listeners
When testing your listeners there are two types of tests that you can perform.
- Do they execute when the configured target(s) execute?
- Does the listener perform the expected?
Testing if listeners execute in response to target(s)
For this case we recommend making use of the mockAction
configuration value that is available on the createStore
configuration. When this is set then any actions that are dispatched will not be executed, instead they will be recorded, along with their payloads.
You can utilise the getMockedActions
function that is bound against your store instance to get the recorded actions, validating that they are what you expect.
This is perfect for us to establish that our listener was fired when a target executed.
We will be showing an actionOn listener within this test, however, this strategy would work equally well for a thunkOn listener.
Given the following model.
import { action, actionOn } from 'easy-peasy';
const model = {
todos: [],
logs: [],
addTodo: action((state, payload) => {
state.todos.push(payload);
}),
onTodoAdded: actionOn(
actions => actions.addTodo,
(state, target) => {
state.logs.push(`Added todo: ${target.payload}`);
},
),
};
We could test the onTodoAdded
action.
test('listener gets dispatched when target fires', () => {
// arrange
const store = createStore(model, {
mockActions: true,
});
// act
store.getActions().addTodo('Write docs');
// assert
expect(store.getMockedActions()).toMatchObject([
{ type: '@action.addTodo', payload: 'Write docs' },
{
type: '@action.onTodoAdded',
payload: {
type: '@action.addTodo',
payload: 'Write docs',
},
},
]);
});
Testing if the listener performs as expected
We may also want to test that our listeners perform the expected. It is possible to dispatch our listeners manually by using the store.getListeners()
API.
When dispatch a listener action it is important to note that a very specific payload structure is expected. This payload becomes the target
argument to the listener handler.
Below is an overview of the payload object that you need to provide when manually dispatching a listener action:
type
(string)The type of the target action being responded to. e.g.
"@actions.todos.addTodo"
payload
(any)This will contain the same payload of the target action being responded to.
result
(any | null)When listening to a thunk, if the thunk succeeded and returned a result, the result will be contained within this property.
error
(Error | null)When listening to a thunk, if the thunk failed, this property will contain the
Error
.resolvedTargets
(Array<string>)An array containing a list of the resolved targets, resolved by the
targetResolver
function. This aids in performing target based logic within a listener handler.
You need not provide all the values if you know that your listener only uses some of them. You could instead only populate the parts of the target
object that you expect your listener to be using.
For example, below we will manually dispatch a listener, providing only the payload.
store.getListeners().onTodoAdded({
payload: 'Write docs on testing'
})
Once you take these rules into account, you could then follow a similar strategy to testing actions for actionOn listeners. Equally, you can follow a similar strategy to testing thunks for thunkOn listeners.
Below we will show an example of how you could test the onTodoAdded
action that we described within the model above.
test('onTodoAdded adds a log entry', () => {
// arrange
const store = createStore(model);
// act
store.getListeners().onTodoAdded({
payload: 'Test listeners',
});
// assert
expect(store.getState().logs).toEqual(['Added todo: Test listeners']);
});