Jest无法检测mock函数调用,Express控制器单元测试问题排查
问题原因分析
你的第二个测试断言失败的核心原因是deleteExercise控制器内部调用的Exercise.deleteExercise是异步操作,测试用例当前以同步逻辑执行断言时,异步操作还未完成,res.send还没有被触发,所以断言失败。
第一个测试用例没有涉及异步逻辑,仅同步校验参数后就调用了res.send,因此可以正常通过。你后续控制台能打印到res.send的调用记录,是因为打印时机在异步逻辑执行完成后,此时断言已经提前执行结束了。
解决方案
根据你的控制器异步实现方式,选择对应修改方案即可:
方案1:控制器返回Promise(async函数写法)
将测试用例改为async/await写法,等待控制器逻辑执行完成后再执行断言:
it('calls the deleteExercise class method if the id was passed', async () => { const req = { body : { id : 1234 } } // 等待异步逻辑执行完毕 await deleteExercise(req, res) expect(Exercise.deleteExercise).toHaveBeenCalledWith(1234) expect(res.send).toHaveBeenCalled() })
同时要确保你已经正确mock了Exercise.deleteExercise,避免走真实数据库逻辑:
// 放在测试用例外层或者beforeEach中 Exercise.deleteExercise = jest.fn().mockResolvedValue(/* 你要返回的mock数据 */)
方案2:控制器使用回调写法
使用Jest提供的done参数标记测试用例的结束时机,等到res.send被调用后再执行断言、结束测试:
it('calls the deleteExercise class method if the id was passed', (done) => { const req = { body : { id : 1234 } } // 监听res.send调用,触发后执行断言 res.send.mockImplementation(() => { expect(Exercise.deleteExercise).toHaveBeenCalledWith(1234) expect(res.send).toHaveBeenCalled() done() // 标记测试结束 }) deleteExercise(req, res) })
额外校验点
- 确认你mock的
Exercise.deleteExercise和控制器内部导入的Exercise是同一个模块实例,避免mock失效 - 不要让mock的
Exercise.deleteExercise返回rejected状态的Promise,否则会导致控制器逻辑走错误分支,不会触发预期的res.send调用
内容的提问来源于stack exchange,提问作者Bharat Soni
相关产品推荐
相关产品推荐

