React/Jest/Enzyme - 等待时间不够长
Posted
技术标签:
【中文标题】React/Jest/Enzyme - 等待时间不够长【英文标题】:React/Jest/Enzyme - await not waiting long enough 【发布时间】:2019-10-27 01:45:06 【问题描述】:我有一个等待多个承诺的函数
const function = async () =>
await function1()
await function2()
await function3()
我想测试一下是否调用了function3:
it(('calls function3', async () =>
jest.spyOn(api, 'function1').mockResolvedValue()
jest.spyOn(api, 'function2').mockResolvedValue()
spy = jest.spyOn(api, 'function3')
await function()
expect(spy).toBeCalledTimes(1)
)
这个测试失败了,但是当我多次调用 await 时:
it(('calls function3', async () =>
jest.spyOn(api, 'function1').mockResolvedValue()
jest.spyOn(api, 'function2').mockResolvedValue()
spy = jest.spyOn(api, 'function3')
await await await await await function()
expect(spy).toBeCalledTimes(1)
)
测试将通过。为什么是这样? await function()
不应该在进入下一个期望行之前解决所有的承诺吗?
编辑:awaited 函数越深,即 function4,我需要的 await 语句越多,但不是 1 比 1。
【问题讨论】:
function
是javascript保留字,你确定你的函数声明没有报错吗?
@Dario 抱歉,这是一个示例,而不是实际代码
【参考方案1】:
在进入下一个期望行之前,
await function()
不应该解决所有的承诺吗?
是的,await
将等待返回的Promise
再继续。
这是一个简单的工作示例:
const function1 = jest.fn().mockResolvedValue();
const function2 = jest.fn().mockResolvedValue();
const function3 = jest.fn().mockResolvedValue();
const func = async () =>
await function1();
await function2();
await function3();
it('calls function3', async () =>
await func();
expect(function3).toHaveBeenCalled(); // Success!
)
如果await
没有像预期的那样等待,那么Promise
链很可能在某个时候中断。
这是一个损坏的Promise
链示例:
const function1 = jest.fn().mockResolvedValue();
const function2 = jest.fn().mockResolvedValue();
const function3 = jest.fn().mockResolvedValue();
const func = async () =>
await function1();
await function2();
await function3();
const func2 = async () =>
func(); // <= breaks the Promise chain
it('calls function3', async () =>
await func2();
expect(function3).toHaveBeenCalled(); // <= FAILS
)
多次调用await
会将剩余的测试函数排在PromiseJobs
队列后面多次,这可以让待处理的Promise
回调有机会运行...
...所以上面的破测试如果改成这样就可以通过了:
it('calls function3', async () =>
await await await await func2(); // <= multiple await calls
expect(function3).toHaveBeenCalled(); // Success...only because of multiple await calls
)
...但真正的解决方案是找到并修复Promise
链中断的位置:
const func2 = async () =>
await func(); // <= calling await on func fixes the Promise chain
it('calls function3', async () =>
await func2();
expect(function3).toHaveBeenCalled(); // Success!
)
【讨论】:
承诺链在等待同步函数时中断? 不,如果await
没有在应该是链的一部分的 Promise
上调用,则 Promise
链会中断。在示例中,从 func
返回的 Promise
链接到从 function1
、function2
和 function3
返回的 Promises
...但 func2
不在func
上调用await
,因此从func2
返回的Promise
不会链接到另一个Promises
。修复func2
以在func
上调用await
修复了链并确保从func2
返回的Promise
一直链接到function3
返回的Promise
。 @peter176【参考方案2】:
promise 在微任务队列中排队的顺序是一个问题,我使用flush-promises
来解决同样的问题。
它使用节点setImmediate
向队列推送一个回调,该回调将在微任务队列为空时调用。
const flushPromises = require('flush-promises');
test('flushPromises', async () =>
let a;
let b;
Promise.resolve().then(() =>
a = 1;
).then(() =>
b = 2;
)
await flushPromises();
expect(a).toBe(1);
expect(b).toBe(2);
);
【讨论】:
有不需要导入其他库的解决方案吗? 这个lib是9行代码:],你可以复制到你的项目中。【参考方案3】:现在有一个 proposal in Jest 有类似 runAllTimers
的东西,但用于承诺。
因此,如果您不想集成flush-promises
,您可以只使用setTimeout(() => ...rest code..., 0)
。由于timeout
是宏任务,因此可以保证在运行之前解决所有待处理的微任务(如承诺)。
更多关于微任务和宏任务:https://abc.danch.me/microtasks-macrotasks-more-on-the-event-loop-881557d7af6f
【讨论】:
以上是关于React/Jest/Enzyme - 等待时间不够长的主要内容,如果未能解决你的问题,请参考以下文章