Jest测试中emit error未触发预期错误处理函数的原因排查
问题根因
- mock实例缺失方法,事件触发无效:你给
fs.createWriteStream的mock返回值仅定义了on、close两个方法,测试代码中调用writer.emit('error')时触发的是不存在的方法,绑定的错误回调自然不会执行。 - pipeHandler赋值时机错误:
beforeEach执行阶段就已经完成了axios的mock注入,此时pipeHandler还未在it块中赋值,传入mock的是undefined,你写的错误触发逻辑根本不会运行。 - 冗余mock函数无绑定:你定义的
writerErrorFn、dataErrorFn没有和事件回调逻辑做任何关联,永远不会被调用,基于这些函数的断言必然失败。
修复方案
不需要完全重构测试,调整mock逻辑和事件触发方式即可:
describe('fetchData', () => { let mockWriterEventHandlers = {}; let mockDataEventHandlers = {}; let writerCloseFn; beforeEach(() => { mockWriterEventHandlers = {}; mockDataEventHandlers = {}; // 绑定事件时把回调存入对应事件的handler对象 const dataOnFn = jest.fn((e, cb) => { mockDataEventHandlers[e] = cb; }); const writerOnFn = jest.fn((e, cb) => { mockWriterEventHandlers[e] = cb; }); writerCloseFn = jest.fn(); // 不需要实际执行pipe逻辑,我们手动触发事件即可 axios.mockImplementationOnce(() => ({ status: 200, data: { pipe: jest.fn(), on: dataOnFn, }, headers: { 'content-length': 100 }, })); fs.createWriteStream.mockImplementationOnce(() => ({ on: writerOnFn, close: writerCloseFn, })); jest.spyOn(console, 'info').mockImplementation(() => {}); jest.spyOn(console, 'log').mockImplementation(() => {}); jest.useFakeTimers(); }); afterEach(() => { jest.useRealTimers(); jest.clearAllMocks(); }); it('handles errors from the writer', async () => { expect.assertions(2); const testError = new Error('bang'); // 先启动下载逻辑,完成所有事件绑定 const downloadPromise = downloadMtgJsonZip(); // 手动触发writer的error事件,执行业务代码中绑定的回调 mockWriterEventHandlers.error(testError); try { await downloadPromise; } catch (err) { // 校验错误被正确抛出 expect(err).toEqual(testError); // 校验writer.close按预期调用 expect(writerCloseFn).toHaveBeenCalled(); } }); });
如果确实需要验证pipe调用后的逻辑,可以把pipe的mock改为接收writer参数后执行对应逻辑,注意要在mock中动态读取pipeHandler,不要在beforeEach阶段就固化值即可。
内容的提问来源于stack exchange,提问作者AncientSwordRage
相关产品推荐
相关产品推荐

