Jest单元测试中重写桩(stub)函数mock行为未按预期生效
问题原因
你的代码不生效有四个核心问题:
jest.mock会被Jest自动提升到文件最顶部执行,测试运行阶段(比如beforeAll钩子中)再次调用jest.mock不会触发模块重新替换,完全无效。- 你在error场景的
beforeEach里提前执行了控制器方法,此时还没修改mock行为,拿到的永远是默认mock的返回值。 - 你写的二次mock返回结构和原有mock不匹配:原有手动mock导出的是构造函数形式的
ShipmentSummaryService,二次mock直接返回了带方法的普通对象,就算mock生效也会导致Nest依赖注入失败。 - 异步方法抛错的断言写法错误,同步写法的
expect(() => fn()).toThrow()捕获不到异步抛出的异常。
修复方案
不需要重新mock整个模块,你已经通过moduleRef.get()拿到了注入的mock服务实例,直接修改实例上对应方法的mock实现即可,这是Jest手动mock场景下切换测试行为的标准做法。
- 保留你现有的顶层
jest.mock声明和__mocks__文件夹下的mock服务实现,不需要改动。 - 重写error场景的测试块代码如下:
describe('error scenario', () => { beforeAll(() => { // 直接修改mock服务实例上的方法行为,覆盖默认的ResolvedValue配置 shipmentSummaryService.getAvailableShipmentModes.mockRejectedValue(new Error('Test error')); }); afterAll(() => { // 测试完成后恢复默认mock实现,避免影响其他测试块 shipmentSummaryService.getAvailableShipmentModes.mockResolvedValue(validModeDropdown()); }); test('throws an error', async () => { // 异步抛错需要用rejects配合await断言 await expect(shipmentSummaryController.getAllShipmentModes('256')).rejects.toThrow('Test error'); }) })
额外说明
- 不要在测试运行阶段(钩子、测试用例内)调用
jest.mock,该API仅在文件加载初始化阶段生效。 - 你顶层
beforeEach里的jest.clearAllMocks()只会清空mock的调用记录、mock实例的调用状态,不会重置你配置的mockResolvedValue/mockRejectedValue行为,所以在单独测试块里修改mock行为后,记得在块结束后恢复默认值,避免测试用例之间互相污染。 - 如果需要在单个测试用例里修改mock行为,直接在
test块内调用对应mock方法的mockReturnValue/ mockResolvedValue即可,作用范围可以通过beforeEach/ afterEach灵活控制。
内容的提问来源于stack exchange,提问作者MetalGeek7
相关产品推荐
相关产品推荐

