使用 sinon.fakeTimers 测试 setInterval() 不起作用?

Testing setInterval() with sinon.fakeTimers not working?

我的 componentDidMount 中有一个动作,它在 30000 毫秒后从 Reddit 获取图像。我想测试这个但无法弄清楚?!?!有什么建议吗?

    componentDidMount() {
       this.getHumorEvery(30000)
    }

    getHumorEvery = async (ms) => {
        await this.props.getHumor()

        return setInterval(() => {
            this.props.getHumor()
    }, ms)

所以 getHumor() 立即得到调用。然后在 30000 毫秒后再次调用它等等。

然后我的测试...

it('should refetch images every 30000 ms', () => {
    const clock = sinon.useFakeTimers();
    const getHumorSpy = sinon.spy();

    renderComponent(requiredProps({ getHumor: getHumorSpy }));
    clock.tick(30000)

    sinon.assert.calledTwice(getHumorSpy)

})

测试失败,因为 getHumor 只调用了一次。 setInterval() 永远不会被解雇?

在 promises 上调用 then 并在异步函数中调用 await 为事件循环排队代码,该事件循环在当前同步代码完成且时钟滴答作响之前不会执行。同样,setTimeout 通常将代码添加到队列中,该队列也必须等到下一个时钟滴答。

Sinon fake timers "are synchronous implementations of setTimeout and friends" "overwrite the global functions" 并管理使用 setTimeout 和类似全局变量排队的代码,并在模拟时钟滴答时同步调用它。

仍在为事件循环排队的代码与之前为事件循环排队但现在被同步调用的代码之间的这种断开可能会导致问题(如 this and this)。

这些问题通常可以通过插入事件循环的手动循环来解决,以允许队列中的任何待处理代码在同步模拟时钟滴答之前执行。

在这种情况下,将测试转换为异步函数并在已解决的 Promise 上调用 await 使队列中的待处理代码有机会在同步时钟滴答之前 运行 允许代码按预期运行并正确断言:

it('should refetch images every 30000 ms', async () => {
    const clock = sinon.useFakeTimers();
    const getHumorSpy = sinon.spy();

    renderComponent(requiredProps({ getHumor: getHumorSpy }));

    // let event loop cycle
    await Promise.resolve();

    clock.tick(30000);
    sinon.assert.calledTwice(getHumorSpy);
})