最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Testing a function which uses setTimeout with Jest: why is this test failing? - Stack Overflow

programmeradmin1浏览0评论

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?

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?

Share Improve this question asked Mar 27, 2019 at 6:41 Mark BellMark Bell 29.8k26 gold badges121 silver badges150 bronze badges
Add a ment  | 

2 Answers 2

Reset to default 8

Your rate limiter uses a trailing method where it cancels any calls currently in progress when new ones e 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!
});

You should use the second call to fire the timers:

More info here

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.runAllTimers();

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

  jest.runAllTimers();

  expect(action).toHaveBeenCalledTimes(2);
});
发布评论

评论列表(0)

  1. 暂无评论