How to propertly test this function.
export function initScheduler(timeout: number, callback: () => Promise<void>): void {
setTimeout(() => {
callback().then(() => {
initScheduler(timeout, callback);
});
}, timeout);
}
I tried something like
describe('initScheduler', () => {
it('should call on schedule', () => {
jest.useFakeTimers();
const timeout: number = 60000;
const callback: jest.Mock = jest.fn().mockResolvedValue(undefined);
initScheduler(timeout, callback);
expect(setTimeout).toHaveBeenCalledTimes(1);
expect(setTimeout).toHaveBeenLastCalledWith(expect.any(Function), timeout);
expect(callback).not.toBeCalled();
jest.advanceTimersByTime(timeout);
expect(callback).toBeCalled();
expect(setTimeout).toHaveBeenCalledTimes(2);
});
});
But last expectation returns 1
Don't concentrate on assertions against setTimeout
. Concentrate on what your function is expected to do.
describe('', () => {
jest.useFakeTimers();
const timeout = 300;
const callback = jest.fn();
beforeEach(() => {
jest.clearAllTimers();
callback
.mockClear()
.mockReturnValue(Promise.resolve());
});
it('runs callback only after delay given', () => {
initScheduler(timeout, callback);
jest.advanceTimersByTime(timeout - 1);
expect(callback).not.toHaveBeenCalled();
jest.advanceTimersByTime(2);
expect(callback).toHaveBeenCalledTimes(1);
});
it('reruns scheduler if callback been resolved successfully', async () => {
initScheduler(timeout, callback);
expect(callback).not.toHaveBeenCalled();
jest.advanceTimersByTime(timeout);
await Promise.resolve();
jest.advanceTimersByTime(timeout);
await Promise.resolve();
jest.advanceTimersByTime(timeout);
await Promise.resolve();
expect(callback).toHaveBeenCalledTimes(3);
});
it('stops scheduler if callback rejected', async () => {
callback.mockReturnValue(Promise.reject());
initScheduler(timeout, callback);
jest.advanceTimersByTime(timeout);
await Promise.resolve();
jest.advanceTimersByTime(timeout);
await Promise.resolve();
expect(callback).toHaveBeenCalledTimes(1);
});
});
Some details.
it()
to me without jest.clearAllTimers()
. Probably, it depends on jsdom
implementation or jest
version, I don't know.await Promise.resolve()
your mock for callback
dos not run .then
part. Actually it could be await <anything else>
, I just see await Promise.resolve();
looking less magic than await 42;
. Anyway, its purpose to flush microtasks queue while jest itself does not provide straightforward API on that.