如何Mock setTimeout回调,覆盖antd message相关代码未测试行?
解决setTimeout回调测试覆盖问题
我来帮你搞定这个测试覆盖的问题——本质是因为setTimeout是异步执行的,常规测试流程跑完的时候,回调里的代码还没来得及执行,所以没被统计到覆盖。下面用Jest(前端最常用的测试框架)给你一步步拆解解决方案:
1. 先Mock antd的message组件
我们不需要真的渲染消息提示,只需要验证它的方法有没有被正确调用,所以先把messageMock掉:
import { message } from 'antd'; // Mock antd的message模块 jest.mock('antd', () => ({ message: { destroy: jest.fn(), error: jest.fn(), info: jest.fn(), }, }));
2. 启用定时器模拟
Jest提供了替换全局定时器的能力,这样我们可以手动控制定时器的执行时机:
// 在测试文件顶部或者测试用例开头启用假定时器 jest.useFakeTimers(); // 可选:监听setTimeout调用,方便后续验证 jest.spyOn(global, 'setTimeout');
3. 编写测试用例并触发回调
调用你要测试的error或info函数后,手动触发定时器的回调,这样就能覆盖到setTimeout里的代码了:
import { error, info } from './your-file-path'; // 替换成你的文件路径 test('error函数能正确触发message.error', () => { // 1. 调用目标函数 error('测试错误提示'); // 先验证message.destroy是否被调用 expect(message.destroy).toHaveBeenCalled(); // 验证setTimeout是否按预期调用(延迟10ms) expect(setTimeout).toHaveBeenCalledTimes(1); expect(setTimeout).toHaveBeenLastCalledWith(expect.any(Function), 10); // 2. 快进所有待执行的定时器,触发回调 jest.runAllTimers(); // 3. 验证回调里的message.error是否被正确调用 expect(message.error).toHaveBeenCalledWith('测试错误提示'); }); test('info函数能正确触发message.info', () => { info('测试信息提示'); expect(message.destroy).toHaveBeenCalled(); jest.runAllTimers(); expect(message.info).toHaveBeenCalledWith('测试信息提示'); });
额外技巧:精准控制定时器
如果你不想一次性跑完所有定时器,也可以用jest.advanceTimersByTime(10)来模拟时间过了10ms,或者jest.runOnlyPendingTimers()只执行当前待处理的定时器,这样更精准。
为什么之前没覆盖?
因为默认情况下,测试会同步执行完所有代码就结束,而setTimeout的回调是异步任务,会被放到事件队列里,测试结束时还没执行,所以没被统计到覆盖。通过Jest的假定时器,我们可以手动触发这个回调,让测试覆盖到这部分代码。
内容的提问来源于stack exchange,提问作者Jedi Yuan
相关产品推荐
相关产品推荐

