如何测试API调用成功/失败后返回布尔值的React异步自定义Hook
问题根源
你遇到的两个问题本质上都是同一个原因导致的:
waitForNextUpdate是异步方法,返回Promise,你的测试用例没有用await等待它执行完成:- 测试执行到
expect(current.isValidPolicyReference).toBeFalsy()时,getPolicyByReference的异步 reject 逻辑还没走完,setIsValid(false)还没触发,自然拿不到false的返回值 - 测试用例同步执行完毕后,测试框架会直接卸载测试的Hook实例,等到异步 reject 触发
setIsValid(false)时,组件已经卸载,就会抛出状态更新警告
- 测试执行到
另外你写的成功用例能通过属于侥幸:因为初始状态就是true,不需要等异步逻辑执行完成,两次断言本来就都是true,和异步逻辑走没走完没关系。
解决方案
不需要改mock getPolicyByReference的逻辑,也不需要直接mock axios,只需要给测试用例加上async/await即可:
// 失败用例修正后 test('should be falsy if the policy reference is invalid', async () => { (getPolicyByReference as jest.Mock).mockImplementationOnce(() => Promise.reject(new Error('policy reference error')) ); const { result, waitForNextUpdate, } = render(); expect(result.current.isValidPolicyReference).toBeTruthy(); // 等待异步逻辑执行完成、状态更新 await waitForNextUpdate(); expect(result.current.isValidPolicyReference).toBeFalsy(); });
同时建议把成功用例也改成async/await的写法,避免后续逻辑调整后出现不稳定的测试结果:
test('should be truthy if the policy reference is valid', async () => { (getPolicyByReference as jest.Mock).mockImplementationOnce(() => Promise.resolve(true) ); const { result, waitForNextUpdate, } = render(); expect(result.current.isValidPolicyReference).toBeTruthy(); await waitForNextUpdate(); expect(getPolicyByReference).toHaveBeenCalledWith({ token, tenant, policyReference, }); expect(result.current.isValidPolicyReference).toBeTruthy(); });
额外优化建议
为了避免生产环境也出现「不能给未挂载组件更新状态」的警告,建议给Hook加上取消更新的逻辑:
React.useEffect(() => { let isMounted = true; async function fetchOnMount(): Promise<void> { try { await getPolicyByReference({ policyReference: policyReference as string, tenant, token, }); } catch (error) { // 只有组件还挂载的时候才更新状态 if (isMounted) setIsValid(false); } } if (tenant !== '' && policyReference !== null && token !== '') { fetchOnMount(); } // 组件卸载时标记为未挂载 return () => { isMounted = false; }; }, [policyReference, tenant, token]);
内容的提问来源于stack exchange,提问作者RyanP13
相关产品推荐
相关产品推荐

