Javascript Jest单元测试-如何在setTimeout重复函数中通过异步函数调用
更新:可以肯定地说,这基本上是Javascript Jest单元测试-如何在setTimeout重复函数中通过异步函数调用,javascript,reactjs,jestjs,Javascript,Reactjs,Jestjs,更新:可以肯定地说,这基本上是 我有一个函数,它在执行异步函数后会自动重复。我想验证jest.advanceTimersOnTime(5000)是否继续生成expect(doAsyncStuff).toHaveBeenCalledTimes(X) 我观察到,我们可以通过调用来重复该函数,但当要求jest提前计时时,它不会“通过”。当您删除它前面的异步函数时,它确实起作用。所以听起来我必须让doAsyncStuff“通过电话”或解决一些悬而未决的承诺 作用 试验 显然,Jest疑难解答引用了这个
我有一个函数,它在执行异步函数后会自动重复。我想验证
jest.advanceTimersOnTime(5000)
是否继续生成expect(doAsyncStuff).toHaveBeenCalledTimes(X)代码>
我观察到,我们可以通过调用来重复该函数,但当要求jest提前计时时,它不会“通过”。当您删除它前面的异步函数时,它确实起作用。所以听起来我必须让doAsyncStuff“通过电话”或解决一些悬而未决的承诺
作用
试验
显然,Jest疑难解答引用了这个问题:我们已将定义设置为在事件循环的下一个勾号时异步发生。我想这也适用于实时代码中的事件,而不仅仅是书面测试
执行jest.runAllTimers()
将使事情陷入无休止的循环
添加jest.runAllTicks()
将提高滴答声,因此上述测试现在可以工作了
我仍然很困惑,但我想这就是答案
jest.advanceTimersByTime(5000);
jest.runAllTicks();
expect(doAsyncStuff).toHaveBeenCalledTimes(1);
jest.advanceTimersByTime(5000);
jest.runAllTicks();
expect(doAsyncStuff).toHaveBeenCalledTimes(2);
这项工作还需要模拟doAsyncStuff的实现,因为我认为doAsyncStuff中的任何异步内容都会排队进入tick事件
doAsyncStuff.mockImplementation(() => {
return new Promise.resolve();
});
听起来我必须…解决一些悬而未决的承诺
是的,没错
简单的回答是,Promise
回调被排队,然后
链接到doAsyncStuff
返回的Promise
,按照编写测试的方式,在测试结束之前,回调永远不会有机会运行
要解决此问题,请在测试期间给Promise
回调一次运行的机会:
updater.js
export const doAsyncStuff = async () => { };
code.js
import { doAsyncStuff } from './updater';
export function repeatMe() {
setTimeout(() => {
doAsyncStuff().then((response) => {
if (response) {
console.log("I get here!");
repeatMe();
}
})
}, 5000);
}
code.test.js
import * as updater from './updater';
import { repeatMe } from './code';
test('repeatMe', async () => {
jest.useFakeTimers();
let doAsyncStuff = jest.spyOn(updater, 'doAsyncStuff');
doAsyncStuff.mockResolvedValue(true);
repeatMe();
jest.advanceTimersByTime(5000);
expect(doAsyncStuff).toHaveBeenCalledTimes(1); // Success!
await Promise.resolve(); // let callbacks in PromiseJobs run
jest.advanceTimersByTime(5000);
expect(doAsyncStuff).toHaveBeenCalledTimes(2); // Success!
await Promise.resolve(); // let callbacks in PromiseJobs run
jest.advanceTimersByTime(5000);
expect(doAsyncStuff).toHaveBeenCalledTimes(3); // Success!
// ... and so on ...
});
发生了什么以及为什么会发生的完整细节可以在
import { doAsyncStuff } from './updater';
export function repeatMe() {
setTimeout(() => {
doAsyncStuff().then((response) => {
if (response) {
console.log("I get here!");
repeatMe();
}
})
}, 5000);
}
import * as updater from './updater';
import { repeatMe } from './code';
test('repeatMe', async () => {
jest.useFakeTimers();
let doAsyncStuff = jest.spyOn(updater, 'doAsyncStuff');
doAsyncStuff.mockResolvedValue(true);
repeatMe();
jest.advanceTimersByTime(5000);
expect(doAsyncStuff).toHaveBeenCalledTimes(1); // Success!
await Promise.resolve(); // let callbacks in PromiseJobs run
jest.advanceTimersByTime(5000);
expect(doAsyncStuff).toHaveBeenCalledTimes(2); // Success!
await Promise.resolve(); // let callbacks in PromiseJobs run
jest.advanceTimersByTime(5000);
expect(doAsyncStuff).toHaveBeenCalledTimes(3); // Success!
// ... and so on ...
});