开玩笑的单元测试-如何在setTimeout重复函数中通过异步函数调用

时间:2019-03-22 14:40:30

标签: javascript reactjs jestjs

更新:可以肯定地说,这基本上是Jest: Timer and Promise don't work well. (setTimeout and async function)的重复


我有一个函数,在执行异步功能后会自我重复。我想验证jest.advanceTimersOnTime(5000)继续产生expect(doAsyncStuff).toHaveBeenCalledTimes(X);

我观察到我们可以到达调用以重复该功能,但是当要求开玩笑地提前计时器时,它不会“通过”。当您删除其前面的异步功能时,它确实起作用。因此,听起来我必须获得doAsyncStuff才能“致电给我”或在此处解决一些未完成的承诺?

功能

function repeatMe() {
    setTimeout(() => {
        doAsyncStuff.then((response) => {
            if (response) {
                console.log("I get here!");
                repeatMe();
            }
        })
    }, 5000);
}

测试

jest.useFakeTimers();

let doAsyncStuff = jest.spyOn(updater, 'doAsyncStuff');
doAsyncStuff.mockResolvedValue(true);

repeatMe();

jest.advanceTimersByTime(5000);
expect(doAsyncStuff).toHaveBeenCalledTimes(1);

jest.advanceTimersByTime(5000);
expect(doAsyncStuff).toHaveBeenCalledTimes(2); // Failed?

2 个答案:

答案 0 :(得分:1)

  

所以听起来我必须...在这里解决一些未完成的承诺?

是的。


简短的答案是,Promise链接到then返回的Promise的{​​{1}}和{编写测试,在测试结束之前,回调永远没有机会运行

要解决此问题,请为doAsyncStuff回调提供在测试过程中运行的机会:

updater.js

Promise

code.js

export const doAsyncStuff = async () => { };

code.test.js

import { doAsyncStuff } from './updater';

export function repeatMe() {
  setTimeout(() => {
    doAsyncStuff().then((response) => {
      if (response) {
        console.log("I get here!");
        repeatMe();
      }
    })
  }, 5000);
}

PromiseJobs

中可以找到确切的发生原因的详细信息。

答案 1 :(得分:0)

很明显,Jest故障排除解决了此问题:we have set the definition to happen asynchronously on the next tick of the event loop。我想这也适用于实时代码中的事件,而不仅仅是书面测试。

jest.runAllTimers()会使事情陷入无休止的循环。

添加jest.runAllTicks()将使刻度线前进,因此上述测试现在可以进行。

我仍然很困惑,但是我想这就是答案。

jest.advanceTimersByTime(5000);
jest.runAllTicks();
expect(doAsyncStuff).toHaveBeenCalledTimes(1);

jest.advanceTimersByTime(5000);
jest.runAllTicks();
expect(doAsyncStuff).toHaveBeenCalledTimes(2); 

https://jestjs.io/docs/en/troubleshooting#defining-tests


要实现此目的,还需要模拟doAsyncStuff的实现,因为我也认为doAsyncStuff中的任何异步内容都会排队到tick事件中。

            doAsyncStuff.mockImplementation(() => {
                return new Promise.resolve();
            });