Jest误判测试失败?NestJS模拟Repository异常问题排查
NestJS Jest测试:捕获异步异常后测试仍失败的问题解决
问题场景
我有一个NestJS TypeScript应用,其中MyService依赖MyRepository。我希望测试当myRepository.upsert(...)抛出异常时,myService.doSomething()能按预期处理错误(捕获异常并设置响应的errorCode)。
我通过jest.spyOn结合mockImplementation模拟异常:
jest.spyOn(myRepository, 'upsert').mockImplementation((myEntity: MyEntity) => Promise.reject(new Error('SAVE FAILED')) )
注意doSomething()既没有重新抛出该异常,也没有抛出其他异常,测试中的断言已验证错误被正确捕获处理,但Jest仍将doSomething_failedUpdate测试标记为失败。
完整代码
export class MyResponse { // ... errorCode: number } // 数据库CRUD仓库 @Injectable() class MyRepository { // ... async upsert(myEntity: MyEntity): Promise<MyEntity> { // ... } } // 依赖仓库的服务 @Injectable() export class MyService { constructor( private myRepository: MyRepository ) async doSomething(): Promise<MyResponse> { let response: MyResponse = new MyResponse() try { // 此处省略myEntity定义逻辑 await this.myRepository.upsert(myEntity) } catch (error) { response.errorCode = 100 } return response } } // Jest测试代码 import { expect } from 'chai' import { INestApplication } from '@nestjs/common' import { Test, TestingModule } from '@nestjs/testing' // 其他导入 describe('Test MyService.doSomething', () => { let appModuleFixture: TestingModule = null let app: INestApplication = null let myService: MyService = null let myRepository: MyRepository = null beforeEach(async () => { appModuleFixture = await Test.createTestingModule({ imports: [AppModule], }).compile() app = appModuleFixture.createNestApplication() app.useGlobalFilters(new DefaultExceptionFilter(), new MyExceptionFilter()) await app.init() myRepository = appModuleFixture.get(MyRepository) myService = appModuleFixture.get(MyService) }) afterEach(async () => { await app.close() jest.resetAllMocks() }) // 其他测试用例:'doSomething_ok'... it('doSomething_failedUpdate', async () => { // GIVEN: jest.spyOn(myRepository, 'upsert').mockImplementation((myEntity: MyEntity) => Promise.reject(new Error('SAVE FAILED')) ) // WHEN: let response = await myService.doSomething() // THEN: expect(response.errorCode).to.be.equal(100) }) })
Jest错误输出
FAIL test/services/my.service.e2e-spec.ts (13.048 s) Test MyService.doSomething ✕ doSomething_failedUpdate (9490 ms) ○ skipped doSomething_ok ● Test MyService.doSomething › doSomething_failedUpdate SAVE FAILED 448 | .spyOn(myRepository, 'upsert') 449 | .mockImplementation((myEntity: MyEntity) => > 450 | Promise.reject(new Error('SAVE FAILED')) | ^ 451 | ) 452 | 453 | // WHEN at MyRepository.<anonymous> (services/my.service.e2e-spec.ts:450:24) ... Test Suites: 1 failed, 1 total Tests: 1 failed, 1 skipped, 2 total
问题原因
问题源于Jest的未处理Promise拒绝检测机制:
- 当使用
mockImplementation手动返回Promise.reject时,Jest会将这个Promise的拒绝视为未被处理的异常,即使你的业务代码已经通过try/catch捕获了该异常。 mockImplementation没有针对异步函数的异常场景做特殊处理,导致Jest的mock系统无法正确追踪这个Promise的处理状态,最终误判为测试失败。
解决方案
有两种可靠的解决方式:
1. 使用Jest原生的mockRejectedValue方法
这是Jest专门为模拟异步函数抛出异常设计的API,能正确处理Promise的拒绝状态,避免触发Jest的未处理Promise拒绝检测:
it('doSomething_failedUpdate', async () => { // GIVEN: jest.spyOn(myRepository, 'upsert').mockRejectedValue(new Error('SAVE FAILED')) // WHEN: const response = await myService.doSomething() // THEN: expect(response.errorCode).to.be.equal(100) })
2. 增加mock函数调用的断言(可选,增强测试严谨性)
通过断言确认mock函数被调用,能让Jest明确这个Promise的拒绝已经被业务代码处理,进一步避免误判:
it('doSomething_failedUpdate', async () => { // GIVEN: const upsertSpy = jest.spyOn(myRepository, 'upsert').mockRejectedValue(new Error('SAVE FAILED')) // WHEN: const response = await myService.doSomething() // THEN: expect(response.errorCode).to.be.equal(100) expect(upsertSpy).toHaveBeenCalled() // 确认mock函数被正确调用 })
另外,确保doSomething函数中的myEntity变量已正确定义(否则会先抛出同步ReferenceError,干扰测试逻辑)。
内容的提问来源于stack exchange,提问作者burebista
相关产品推荐
相关产品推荐

