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

And*_*ert 1 setinterval sinon reactjs

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

    componentDidMount() {
       this.getHumorEvery(30000)
    }

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

        return setInterval(() => {
            this.props.getHumor()
    }, ms)
Run Code Online (Sandbox Code Playgroud)

所以 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)
Run Code Online (Sandbox Code Playgroud)

})

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

Bri*_*ams 6

在异步函数中调用 then 和调用 await 将事件循环的代码排队,直到当前同步代码完成并且时钟滴答时才执行。类似地,setTimeout 通常会向队列中添加代码,这些代码也必须等到下一个时钟滴答。

Sinon 假定时器“是 setTimeout 和朋友的同步实现”,它“覆盖全局函数”并管理用 setTimeout 和类似全局函数排队的代码,并在模拟时钟滴答时同步调用它。

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

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

在这种情况下,将测试转换为异步函数并在已解决的 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);
})
Run Code Online (Sandbox Code Playgroud)

  • 我会尝试用“clock.tickAsync”替换“clock.tick”,并避免额外的“await Promise.resolve();” (3认同)