Javascript 以承诺取笑假计时器
在使用假计时器和承诺的组合时,让Jest测试框架(23.2.0版)正常工作有点困难。我哪里做错了 假设我有以下模块:Javascript 以承诺取笑假计时器,javascript,promise,jestjs,Javascript,Promise,Jestjs,在使用假计时器和承诺的组合时,让Jest测试框架(23.2.0版)正常工作有点困难。我哪里做错了 假设我有以下模块: // timing.js export const timeout = ms => new Promise(resolve => { setTimeout(resolve, ms) }) 我的测试文件如下所示: // timing.test.js import { timeout } from './timing' describe('time
// timing.js
export const timeout = ms =>
new Promise(resolve => {
setTimeout(resolve, ms)
})
我的测试文件如下所示:
// timing.test.js
import { timeout } from './timing'
describe('timeout()', () => {
beforeEach(() => {
jest.useFakeTimers()
})
it('resolves in a given amount of time', () => {
const spy = jest.fn()
timeout(100).then(spy)
expect(spy).not.toHaveBeenCalled()
jest.advanceTimersByTime(100)
expect(spy).toHaveBeenCalled()
})
})
此操作失败,输出如下:
● timeout › resolves in a given amount of time
expect(jest.fn()).toHaveBeenCalled()
Expected mock function to have been called, but it was not called.
15 |
16 | jest.advanceTimersByTime(100)
> 17 | expect(spy).toHaveBeenCalled()
| ^
18 | })
19 | })
20 |
at Object.<anonymous> (src/timing.test.js:17:17)
。。。考试会通过的
timeout
✓ resolves in a given amount of time (5ms)
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 1.304s
更新
虽然这不是最优雅的解决方案,但我现在使用下面的测试。它是有效的,但我仍然很好奇为什么原来的一个没有
import { timeout } from './timing'
describe('timeout', () => {
it('resolves in a given amount of time', done => {
setTimeout(() => done(new Error('it didn\'t resolve or took longer than expected')), 10)
return timeout(9).then(done)
})
})
目前,它不受支持
你没有做错任何事-现在不起作用-对不起。在我们开始工作之前,必须发生以下事情:
- Jest需要合并正在进行的工作,将lolex合并为其伪计时器实现
- Lolex需要支持兑现承诺——我们在最近的Node.js collaborator峰会上与V8团队讨论了这一点。这将公开一个钩子,我们将使用它来执行类似于
的操作,并让它与承诺一起工作advanceTimeByTime(100)
.then(spy)
只会在以后调用
由于我们是志愿者,这些事情没有具体的时间表。我希望SimenB在未来2-3个月内完成合并,下个月我将与V8团队一起跟进hook
你现在能做什么
您始终可以编写异步测试:
// note this is an async function now
it('resolves in a given amount of time', async () => {
// this is in a promise.reoslve.then to not 'lock' on the await
Promise.resolve().then(() => jest.advanceTimersByTime(100));
await timeout(100);
});
如果还有其他需要等待的内容,可以在超时后添加期望值。在我的例子中,计时器回调调用了其他异步函数,因此其他解决方案对我不起作用。我最终发现,通过手动确保承诺队列为空,所有异步代码都将完成运行,我可以让测试正常工作:
function flushPromises() {
// Wait for promises running in the non-async timer callback to complete.
// From https://stackoverflow.com/a/58716087/308237
return new Promise(resolve => setImmediate(resolve));
}
test('example', async () => {
jest.useFakeTimers();
example_function_to_set_a_timer();
// Wait for one virtual second
jest.advanceTimersByTime(1000);
// Wait for any async functions to finish running
await flushPromises();
// Continue with tests as normal
expect(...);
});
因为您可以在两种不同的伪计时器实现之间进行选择
我发现,jest.useFakeTimers('legacy')
与使用的承诺一起工作,但它不与日期一起工作,而jest.useFakeTimers('modern')
与日期一起工作,但不与承诺一起工作,因为等待flushPromises()
从未解决
我发现最好的解决方案是改用它,因为它既适用于承诺,也适用于日期,没有任何解决方法或黑客:
从“@sinonjs/fake timers”导入FakeTimers;
//测试前:
const clock=FakeTimers.install();
//在测试中:
等待时钟。异步(100);
//测试后:
clock.uninstall();
您不能执行以下操作:const promise=createTimerPromise()jest.advanceTimerByTime(100)wait promise您可以,这将是一个额外的行,我希望能够直接等待函数结果。有人知道它是否仍然是真的,Jest不支持它吗?@jesper它是真的,但仍然不支持它-你需要使用变通方法。是的,这对我测试承诺有很大帮助。非常感谢。
function flushPromises() {
// Wait for promises running in the non-async timer callback to complete.
// From https://stackoverflow.com/a/58716087/308237
return new Promise(resolve => setImmediate(resolve));
}
test('example', async () => {
jest.useFakeTimers();
example_function_to_set_a_timer();
// Wait for one virtual second
jest.advanceTimersByTime(1000);
// Wait for any async functions to finish running
await flushPromises();
// Continue with tests as normal
expect(...);
});