我正在尝试了解 Jest 的异步测试。
我的模块有一个函数,它接受一个布尔值并返回一个值的 Promise。执行器函数调用
setTimeout
,并且在超时回调中,promise 根据最初提供的布尔值来解析或拒绝。代码如下所示:
const withPromises = (passes) => new Promise((resolve, reject) => {
const act = () => {
console.log(`in the timout callback, passed ${passes}`)
if(passes) resolve('something')
else reject(new Error('nothing'))
}
console.log('in the promise definition')
setTimeout(act, 50)
})
export default { withPromises }
我想使用 Jest 对此进行测试。我想我需要使用 Jest 提供的模拟计时器,所以我的测试脚本看起来有点像这样:
import { withPromises } from './request_something'
jest.useFakeTimers()
describe('using a promise and mock timers', () => {
afterAll(() => {
jest.runAllTimers()
})
test('gets a value, if conditions favor', () => {
expect.assertions(1)
return withPromises(true)
.then(resolved => {
expect(resolved).toBe('something')
})
})
})
无论我是否致电
jest.runAllTimers()
,我都会收到以下错误/失败的测试
Timeout - Async callback was not invoked within timeout specified by jasmine.DEFAULT_TIMEOUT_INTERVAL.
你能解释一下我哪里出错了,以及我可以做什么才能通过测试,让承诺按预期解决?
对
jest.useFakeTimers()
的调用会用您必须控制的功能来模拟每个计时器功能。您可以手动提前计时器,而不是自动运行计时器。 jest.runTimersToTime(msToRun)
函数会将其提前 msToRun
毫秒。很常见的是,您希望快进直到每个计时器都结束,并且计算所有计时器完成所需的时间会很麻烦,因此 Jest 提供了 jest.runAllTimers()
,它假装已经过去了足够的时间。
测试中的问题是,您从未在测试中调用
jest.runAllTimers()
,而是在 afterAll
钩子中调用它,该钩子在测试完成后称为 after。在测试期间,计时器保持为零,因此您的回调永远不会被实际调用,并且 Jest 会在预定义的时间间隔(默认值:5 秒)后中止它,以防止陷入潜在的无休止的测试中。只有在测试超时后,您才调用 jest.runAllTimers()
,此时它不会执行任何操作,因为所有测试都已完成。
您需要做的是启动 Promise,然后提前计时器。
describe('using a promise and mock timers', () => {
test('gets a value, if conditions favor', () => {
expect.assertions(1)
// Keep a reference to the pending promise.
const pendingPromise = withPromises(true)
.then(resolved => {
expect(resolved).toBe('something')
})
// Activate the timer (pretend the specified time has elapsed).
jest.runAllTimers()
// Return the promise, so Jest waits for its completion and fails the
// test when the promise is rejected.
return pendingPromise
})
})
所以,我遇到了类似的问题,我想用
setTimeout()
伪造一个异步函数 - 返回一个承诺。但我一直收到Exceeded timeout...
错误。
我在使用
async await
语法修复它时遇到了一些问题。最后,我通过将 const x = await ...
分成两行并将 jest.runAllTimers()
放在它们之间来解决它。 (您也可以用同样的方法使用jest.advanceTimersByTime()
)。
这是一个完整的测试,我用 setTimeout 伪造了一个承诺:
it('test promise with set timeout', async () => {
jest.useFakeTimers();
const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
const asyncFunction = async () => {
await sleep(100);
return true;
};
const resultPromise = asyncFunction();
jest.runAllTimers();
const result = await resultPromise;
expect(result).toBe(true);
jest.useRealTimers();
});
PS。如果您有多个测试,那么将
useFakeTimers
和 useRealTimers
放入 beforeEach
和 afterEach
块中可能会更好。