Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Testing a function which uses setTimeout with Jest: why is this test failing?

I have a rateLimit function (which is just a modified version of this code):

function rateLimit(func, wait) {
    var timeout;
    return function () {
        var context = this;
        var args = arguments;
        var later = function () {
            timeout = null;
            func.apply(context, args);
        };
        clearTimeout(timeout);
        timeout = setTimeout(later, wait);
    };
}

This function works perfectly well in my app, so I'm fairly certain the implementation is fine. However, the following test fails:

jest.useFakeTimers();

test('rateLimit', () => {
    const action = jest.fn();

    const doAction = rateLimit(action, 100);

    doAction(); // This should increment the call count
    doAction(); // This shouldn't, because 100ms hasn't elapsed yet

    jest.advanceTimersByTime(101);

    doAction(); // This should increment the count again

    expect(action).toHaveBeenCalledTimes(2);
});

With the error:

Expected mock function to have been called two times, but it was called one time.

Here is a runnable version of this code on repl.it.

What am I doing wrong here?

like image 944
Mark Bell Avatar asked Mar 27 '19 06:03

Mark Bell


1 Answers

Your rate limiter uses a trailing method where it cancels any calls currently in progress when new ones come in...until the wait time has expired at which point the function is called.

You just need to advance the timers again so the function gets called again:

jest.useFakeTimers();

test('rateLimit', () => {
    const action = jest.fn();

    const doAction = rateLimit(action, 100);

    doAction(); // This should increment the call count
    doAction(); // This shouldn't, because 100ms hasn't elapsed yet

    jest.advanceTimersByTime(101);

    doAction(); // This should increment the count again

    jest.advanceTimersByTime(101);  // <= advance the timers again

    expect(action).toHaveBeenCalledTimes(2);  // Success!
});
like image 77
Brian Adams Avatar answered Sep 22 '22 14:09

Brian Adams