Given a function that has both async/await and timeout, to test it with Jest, we need to know the following:
- use
jest.useFakeTimers()
to get control of the timing - for each timeout, use the Jest Timer Control that applies
jest.runAllTimers()
-- Fast-forward until all timers have been executedjest.runOnlyPendingTimers()
-- Fast forward and exhaust only currently pending timers (but not any new timers that get created during that process)jest.advanceTimersByTime(1000)
-- Fast-forward the given amount of milliseconds- for each
await
in the tested function, addawait Promise.resolve()
in the test, or any other way to resolve the promise before the test execution continues
The function to be tested:
async function infiniteAsyncTimerGame (beginningCallback, endingCallback) {
console.log('start round');
beginningCallback && await beginningCallback();
console.log('awaited beginningCallback, will now set timeout');
setTimeout(async () => {
console.log('continuing after timeout');
endingCallback && await endingCallback();
console.log('awaited endingCallback, will now call function again');
infiniteAsyncTimerGame(beginningCallback, endingCallback);
}, 10000);
console.log('end round');
}
The test:test('infiniteAsyncTimerGame', async () => {
// Preparations
jest.useFakeTimers();
let beginCounter = 0;
let endCounter = 0;
async function beginningCallback () {
await new Promise(resolve => resolve(++beginCounter));
}
async function endingCallback () {
await new Promise(resolve => resolve(++endCounter));
}
// Begin testing
// notice the await keyword in front of the method call
await infiniteAsyncTimerGame(beginningCallback, endingCallback);
// At this point in time, there should have been a call to beginningCallback
// We need to trigger the processing of each `await` keyword in the tested code
// with `await Promise.resolve()` in the test run to flush the Promise queue
// after that we can check for the value the Promise returned
await Promise.resolve();
console.log('check beginCounter');
expect(beginCounter).toEqual(1);
// After the beginningCallback, there should have been a single call to
// setTimeout to schedule the next round in 10 seconds
console.log('check timeout');
expect(setTimeout).toHaveBeenCalledTimes(1);
expect(setTimeout).toHaveBeenLastCalledWith(expect.any(Function), 10000);
// Fast forward and exhaust only currently pending timers
// (but not any new timers that get created during that process)
console.log('runOnlyPendingTimers');
jest.runOnlyPendingTimers();
// At this point in time, there should have been a call to endingCallback
await Promise.resolve();
console.log('check endCounter');
expect(endCounter).toEqual(1);
// After the endingCallback, the next round should be started
// with a new call to beginningCallback
await Promise.resolve();
console.log('check beginCounter');
expect(beginCounter).toEqual(2);
console.log('check timeout');
expect(setTimeout).toHaveBeenCalledTimes(2);
expect(setTimeout).toHaveBeenLastCalledWith(expect.any(Function), 10000);
console.log('runOnlyPendingTimers');
jest.runOnlyPendingTimers();
await Promise.resolve();
console.log('check endCounter');
expect(endCounter).toEqual(2);
// third round
await Promise.resolve();
console.log('check beginCounter');
expect(beginCounter).toEqual(3);
console.log('check timeout');
expect(setTimeout).toHaveBeenCalledTimes(3);
expect(setTimeout).toHaveBeenLastCalledWith(expect.any(Function), 10000);
console.log('runOnlyPendingTimers');
jest.runOnlyPendingTimers();
await Promise.resolve();
console.log('check endCounter');
expect(endCounter).toEqual(3);
});
No comments:
Post a Comment