使用 setTimeout 和 Jest 测试 Promise

问题描述 投票:0回答:2

我正在尝试了解 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.

你能解释一下我哪里出错了,以及我可以做什么才能通过测试,让承诺按预期解决?

javascript testing asynchronous jestjs
2个回答
38
投票

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

0
投票

所以,我遇到了类似的问题,我想用

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
块中可能会更好。

© www.soinside.com 2019 - 2024. All rights reserved.