Javascript 在再次推进模拟计时器之前,等待异步setInterval回调

Javascript 在再次推进模拟计时器之前,等待异步setInterval回调,javascript,node.js,jestjs,mocking,setinterval,Javascript,Node.js,Jestjs,Mocking,Setinterval,我发现,如果不涉及async/await,尝试用Jest测试计时器就足够困难了 我有一个setInterval包在承诺中。setInterval的回调仅在与要解析的条件匹配时才会解析(如下所示)。如果承诺没有得到解决,它将继续尝试迭代,直到它得到解决 index.js jest.useFakeTimers() const _test = require('./index.js') describe('test()', () => { test("Should call

我发现,如果不涉及
async/await
,尝试用Jest测试计时器就足够困难了

我有一个
setInterval
包在承诺中。
setInterval
的回调仅在与要解析的条件匹配时才会解析(如下所示)。如果承诺没有得到解决,它将继续尝试迭代,直到它得到解决

index.js

jest.useFakeTimers()

const _test = require('./index.js')

describe('test()', () => {
    test("Should call 'asyncTask' thrice (1)", () => {
        const promise1 = new Promise((resolve, reject) => {
            // I cannot await `_test()` here as the fast-forward of the timer would never happen
            // So I would actually have to wait 10 seconds
            _test().then(resolve)
        
            jest.advanceTimersByTime(10000)
        })
        const promise2 = new Promise((resolve, reject) => {
            _test().then(resolve)
            
            jest.advanceTimersByTime(10000)
        })
        const promise3 = new Promise((resolve, reject) => {
            _test().then(resolve)
            
            jest.advanceTimersByTime(10000)
        })
        // I have to do it like this over a `Promise.all()` I imagine as I can't risk having them run in parallel
        // I imagine that has the potential to mess up the mock function call count of `asyncTask` so would not be reliable
        const result1 = await promise1
        const result2 = await promise2
        const result3 = await promise3

        expect(result1).toBeUndefined()
        expect(result2).toBeUndefined()
        expect(result3).toBeUndefined()
    })

    test('Should call `asyncTask` thrice (2)', async () => {
        _test().then(res => {
            expect(res).toBeInstanceOf(Object)
        })

        await jest.advanceTimersByTime(10000)
        await jest.advanceTimersByTime(10000)
        await jest.advanceTimersByTime(10000)
        await jest.advanceTimersByTime(10000)
    })
})
module.exports=异步函数(){
让间隔
const_promise=新承诺((解决、拒绝)=>{
间隔=设置间隔(异步()=>{
试一试{
常量结果=等待异步任务()
if(result&&result.val==='Foo Bar'){
清除间隔(间隔)
解决(结果)
}
}捕捉(错误){
拒绝(错误)
}
},10000)//10秒
})
const result=wait\u promise
如果(结果){
返回结果
}
}
我用类似以下内容模拟了
asyncTask
的返回:

\uuuuu mocks\uuuu/asyncTask.js

jest.useFakeTimers()

const _test = require('./index.js')

describe('test()', () => {
    test("Should call 'asyncTask' thrice (1)", () => {
        const promise1 = new Promise((resolve, reject) => {
            // I cannot await `_test()` here as the fast-forward of the timer would never happen
            // So I would actually have to wait 10 seconds
            _test().then(resolve)
        
            jest.advanceTimersByTime(10000)
        })
        const promise2 = new Promise((resolve, reject) => {
            _test().then(resolve)
            
            jest.advanceTimersByTime(10000)
        })
        const promise3 = new Promise((resolve, reject) => {
            _test().then(resolve)
            
            jest.advanceTimersByTime(10000)
        })
        // I have to do it like this over a `Promise.all()` I imagine as I can't risk having them run in parallel
        // I imagine that has the potential to mess up the mock function call count of `asyncTask` so would not be reliable
        const result1 = await promise1
        const result2 = await promise2
        const result3 = await promise3

        expect(result1).toBeUndefined()
        expect(result2).toBeUndefined()
        expect(result3).toBeUndefined()
    })

    test('Should call `asyncTask` thrice (2)', async () => {
        _test().then(res => {
            expect(res).toBeInstanceOf(Object)
        })

        await jest.advanceTimersByTime(10000)
        await jest.advanceTimersByTime(10000)
        await jest.advanceTimersByTime(10000)
        await jest.advanceTimersByTime(10000)
    })
})
module.exports=jest.fn()
.mockReturnValueOnce()
.mockReturnValueOnce()
.嘲笑我一次({
瓦尔:“你好,世界”
})
.mockReturnValue({
瓦尔:“富吧”
})
我试图测试的是
setInterval
的每次迭代的回调&我想尝试控制回调中的所有操作在我再次提前计时器之前完成&下一次
setInterval
发生。例如,使用上面的mock,第三次调用该函数时将返回truthy结果。第四次调用也将是真实的结果&将符合要清除的间隔的条件和要解决的承诺。例如,我想做的一个测试是让
setInterval
迭代3次&即使
asyncTask
返回一个对象,承诺仍然没有得到解决。下面是我如何尝试的几个例子:

index.test.js

jest.useFakeTimers()

const _test = require('./index.js')

describe('test()', () => {
    test("Should call 'asyncTask' thrice (1)", () => {
        const promise1 = new Promise((resolve, reject) => {
            // I cannot await `_test()` here as the fast-forward of the timer would never happen
            // So I would actually have to wait 10 seconds
            _test().then(resolve)
        
            jest.advanceTimersByTime(10000)
        })
        const promise2 = new Promise((resolve, reject) => {
            _test().then(resolve)
            
            jest.advanceTimersByTime(10000)
        })
        const promise3 = new Promise((resolve, reject) => {
            _test().then(resolve)
            
            jest.advanceTimersByTime(10000)
        })
        // I have to do it like this over a `Promise.all()` I imagine as I can't risk having them run in parallel
        // I imagine that has the potential to mess up the mock function call count of `asyncTask` so would not be reliable
        const result1 = await promise1
        const result2 = await promise2
        const result3 = await promise3

        expect(result1).toBeUndefined()
        expect(result2).toBeUndefined()
        expect(result3).toBeUndefined()
    })

    test('Should call `asyncTask` thrice (2)', async () => {
        _test().then(res => {
            expect(res).toBeInstanceOf(Object)
        })

        await jest.advanceTimersByTime(10000)
        await jest.advanceTimersByTime(10000)
        await jest.advanceTimersByTime(10000)
        await jest.advanceTimersByTime(10000)
    })
})
后者可能更接近于我想要实现的目标,如前一个例子
promise1
promise2
,&
promise3
将永远无法解析,因此测试将永远无法完成

如果我使用实时计时器(
jest.useRealTimers()
),而不是假计时器,那么我可以成功地测试这个问题,但显而易见的问题是,我必须等待40秒才能解决承诺&测试才能完成


有什么干净的方法可以实现我在这里想要的吗?感谢您的帮助:)

首先,在setInterval中包装async是一种反模式。等待延迟承诺的循环会更干净。不清楚为什么要多次调用
\u test()
。如果测试不同的条件,则它们属于单独的测试。伪计时器的问题可能是,在链接使用伪计时器解决的承诺之前,您需要再等待一段时间,
flush promises
通常用于此目的。@EstusFlask测试的主要目标是承诺解决的条件。在上面的代码中,我想测试
\u test
只会在
setInterval
的第四次回调中得到一个解析值,因为它将第四次调用
asyncTask
,用
val
作为“Foo Bar”模拟返回值对象,在此条件下,承诺(
\u test()
)被解析,这就是我要测试的。解析函数的条件。我将研究
flush承诺
。感谢您对反模式的评论!我将改变我所看到的,在这种情况下,最好将mockreturnvalueone移动到依赖于它们的测试,否则将限制以特定顺序使用全局mock。