使用Jest测试使用setTimeout的函数:为什么此测试失败?

时间:2019-03-27 06:41:27

标签: javascript unit-testing jestjs

我有一个rateLimit函数(它只是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);
    };
}

此功能在我的应用程序中运行良好,因此,我相当确信实现可以。但是,以下测试失败:

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);
});

出现错误:

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

我在这里做什么错了?

2 个答案:

答案 0 :(得分:3)

您的速率限制器使用一种trailing方法,当有新呼叫进入时,它会取消当前正在进行的任何呼叫...直到等待时间到期,此时将调用该函数。

您只需要再次提前计时器,即可再次调用该函数:

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!
});

答案 1 :(得分:0)

您应该使用第二个调用来启动计时器:

更多信息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);
});