I have a service that can decorate an async function with a configuration-toggled alternate behavior:
// decorator.js
const config = require('./config');
const logger = require('./logger');
function addAlternateBehavior(originalAsyncFunction, alternateAsyncFunction) {
return async () => {
if (config.useAlternateBehavior) {
await alternateAsyncFunction();
} else {
await originalAsyncFunction();
}
logger.info('Behavior finished executing');
};
}
exports.addAlternateBehavior = addAlternateBehavior;
I have a Jest unit test that verifies that the alternate behavior gets called when configured accordingly:
// decorator.test.js
const decorator = require('./decorator');
const config = require('./config');
it('returned function should use alternate behavior when configured to do so', async () => {
// Arrange
const originalAsyncFunction = jest.fn();
const alternateAsyncFunction = jest.fn();
config.useAlternateBehavior = true;
// Act
const decoratedFunction = decorator
.addAlternateBehavior(originalAsyncFunction, alternateAsyncFunction);
await decoratedFunction();
// Assert
expect(originalAsyncFunction.mock.calls.length).toBe(0);
expect(alternateAsyncFunction.mock.calls.length).toBe(1);
});
I want to assert that when you call the decorated function with await
, it also awaits the expected behavior. However, in the decorator, if I change await alternateAsyncFunction();
to just alternateAsyncFunction()
, my unit test still passes.
How can I assert, in a unit test, that the function decorated by addAlternateBehavior()
awaits the alternateAsyncFunction
or originalAsyncFunction
?
Give your async
functions an implementation that waits at least two event loop cycles before calling an inner mock
. Then test if that inner mock
was called:
const decorator = require('./decorator');
const config = require('./config');
it('returned function should use alternate behavior when configured to do so', async () => {
// Arrange
const originalInner = jest.fn();
const originalAsyncFunction = jest.fn(async () => {
await Promise.resolve();
await Promise.resolve();
originalInner();
});
const alternateInner = jest.fn();
const alternateAsyncFunction = jest.fn(async () => {
await Promise.resolve();
await Promise.resolve();
alternateInner();
});
config.useAlternateBehavior = true;
// Act
const decoratedFunction = decorator
.addAlternateBehavior(originalAsyncFunction, alternateAsyncFunction);
await decoratedFunction();
// Assert
expect(originalAsyncFunction.mock.calls.length).toBe(0);
expect(originalInner.mock.calls.length).toBe(0);
expect(alternateAsyncFunction.mock.calls.length).toBe(1);
expect(alternateInner.mock.calls.length).toBe(1);
});
If the function created by addAlternateBehavior()
doesn't await
then the inner mock
won't get called.
Note that two await Promise.resolve();
statements are necessary since the first one resolves during the event loop cycle that runs during await decoratedFunction();