Skip to content

Unable to mock setTimeout after upgrading to v8.0.0 #992

Closed
@evoyy

Description

@evoyy

I use the following technique to temporarily override the global setTimeout to always use a delay of zero. This makes all my transitions instant and my test suite runs nice and fast.

I have the following helper function:

function useZeroTimers(cb) {
    return new Promise((resolve, reject) => {
        let setTimeout = globalThis.setTimeout;
        globalThis.setTimeout = cb => setTimeout(cb, 0);

        Promise.resolve(cb())
            .then(() => {
                globalThis.setTimeout = setTimeout;
                resolve();
            })
            .catch(error => {
                globalThis.setTimeout = setTimeout;
                reject(error);
            });
    });
}

And I use it in my tests like this:

it('should do something', async function() {
    await useZeroTimers(async () => {

        // Any calls to setTimeout within this scope will use the
        // temporarily-overridden setTimeout with a delay of zero.

        doSomething();

        await waitFor(() => {
            expect(something).to.have.been.called;
        });
    });

    // outside the scope, setTimeout is magically restored
});

It's a nice technique that I think is simple and elegant and has worked well for me for a long time.

After upgrading react-testing-library to v12.0.0, this technique no longer works. All my tests that use my useZeroTimers helper are broken.

I noticed the following in the release notes for v8.0.0:

The timeout in waitFor(callback, { interval, timeout } ) now uses the same clock as interval. Previously timeout was always using the real clock while interval was using the global clock which could've been mocked out. For the old behavior I'd recommend waitFor(callback, { interval, timeout: Number.POSITIVE_INFINITY }) and rely on your test runner to timeout considering real timers.

I'm not entirely sure what's going on, but I think that as my useZeroTimers technique modifies the global setTimeout, it is not compatible with v8.0.0. Is there any workaround that can be suggested for this? If not, could I suggest an option to tell waitFor to behave as it did before v8.0.0?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions