jest : how to cheat with time

Of course I could spend some time mirroring the jest docsite, even though the description is comprehensive and the examples are relevant. (https://jestjs.io/docs/timer-mocks)

jest.useFakeTimers();
jest.spyOn(global, 'setTimeout');

Once you have this code, you can freeze the time and execute tasks that should have been deferred to later in real conditions
You can even advance in time by as many milleseconds you need

  jest.advanceTimersByTime(1000);

Never forget to revert the mode back to real timers or the rest of your tests will be run using a fake mode, which can lead to erratic test errors.

afterEach(() => {  jest.useRealTimers();});

That works nice with promises timeouts that you have configured, but you may want to make the overhead look smaller.

Ultimately, what you want to do is to verify conditions after triggering some async events, without having to waste line of codes in mocking objects.

withImmediatelyExecutedTimeouts(() => {
window.resizeTo(300, 300);
window.dispatchEvent(new Event('resize'));
});


I have found an alias function to let you do that, here is the implementation of the function withImmediatelyExecutedTimeouts. Just copy / paste it if you need to test it.

type SetTimeoutMock = jest.SpyInstance<NodeJS.Timeout, [callback: (args: void) => void, ms?: number]>

export const before: () => SetTimeoutMock = () =>
    jest.spyOn(global, 'setTimeout').mockImplementation((callback) => {
        callback();
        return {} as NodeJS.Timeout;
    })

export const after: (mock: SetTimeoutMock) => void = (mock) =>
    mock.mockRestore();


export const withImmediatelyExecutedTimeouts = (action: Function) => {
    const mockTimeouts = before();
    let returnValue: any;
    try {
        returnValue = action();
        if (!(returnValue instanceof Promise)) {
            after(mockTimeouts);
            return returnValue;
        }
    } catch (e) {
        after(mockTimeouts);
        throw e;
    }
    returnValue.then((result) => {
        after(mockTimeouts);
        return result;
    }).catch((error) => {
        after(mockTimeouts);
        throw error;
    })

}

The meaning of the above code is very simple : global.setTimeout will be replaced by the invokation of the nested function. You can run whatever statement that contains timeouts, they will be executed right away instead of asynchronously.
You can even pass a function that returns a promise.

Leave a Reply

Your email address will not be published. Required fields are marked *

*

This site uses Akismet to reduce spam. Learn how your comment data is processed.