如何编写Jest测试验证NestJS控制器的Interceptor已被调用?
验证NestJS拦截器绑定生效的Jest测试方案
针对你要验证LoggingInterceptor是否被正确调用的需求,这里提供几种实用的Jest测试方案:
1. 模拟拦截器类并跟踪调用
通过jest.mock直接模拟拦截器的实现,拦截其导入并跟踪intercept方法的调用情况,适合单元测试场景:
// app.controller.spec.ts import { Test, TestingModule } from '@nestjs/testing'; import { AppController } from './app.controller'; import { AppService } from './app.service'; import { LoggingInterceptor } from './transform.interceptor'; // 模拟LoggingInterceptor,实现mock的intercept方法 jest.mock('./transform.interceptor', () => ({ LoggingInterceptor: jest.fn().mockImplementation(() => ({ intercept: jest.fn((context, next) => next.handle()), })), })); describe('AppController', () => { let appController: AppController; let loggingInterceptor: LoggingInterceptor; beforeEach(async () => { const module: TestingModule = await Test.createTestingModule({ controllers: [AppController], providers: [AppService], }).compile(); appController = module.get(AppController); // 获取mock生成的拦截器实例 loggingInterceptor = (LoggingInterceptor as jest.Mock).mock.instances[0]; }); describe('getHello', () => { it('应触发LoggingInterceptor的intercept方法', async () => { await appController.getHello(); expect(loggingInterceptor.intercept).toHaveBeenCalled(); }); }); });
2. 用SuperTest发起真实请求验证
如果需要验证完整的HTTP请求流程中拦截器是否生效,可以用SuperTest编写e2e测试:
// app.e2e-spec.ts import { Test, TestingModule } from '@nestjs/testing'; import { INestApplication } from '@nestjs/common'; import * as request from 'supertest'; import { AppModule } from './../src/app.module'; import { LoggingInterceptor } from './../src/transform.interceptor'; // 模拟拦截器类 jest.mock('./../src/transform.interceptor', () => ({ LoggingInterceptor: jest.fn().mockImplementation(() => ({ intercept: jest.fn((context, next) => next.handle()), })), })); describe('AppController (e2e)', () => { let app: INestApplication; let loggingInterceptor: LoggingInterceptor; beforeEach(async () => { const moduleFixture: TestingModule = await Test.createTestingModule({ imports: [AppModule], }).compile(); app = moduleFixture.createNestApplication(); await app.init(); loggingInterceptor = (LoggingInterceptor as jest.Mock).mock.instances[0]; }); it('GET / 应触发LoggingInterceptor', async () => { await request(app.getHttpServer()).get('/').expect(200); expect(loggingInterceptor.intercept).toHaveBeenCalled(); }); });
3. 覆盖测试模块中的拦截器依赖
如果拦截器是全局注册或通过APP_INTERCEPTOR令牌提供的,可以通过overrideProvider替换为mock实例:
// app.controller.spec.ts import { Test, TestingModule } from '@nestjs/testing'; import { AppController } from './app.controller'; import { AppService } from './app.service'; import { LoggingInterceptor } from './transform.interceptor'; import { APP_INTERCEPTOR } from '@nestjs/core'; // 定义mock拦截器实例 const mockLoggingInterceptor = { intercept: jest.fn((context, next) => next.handle()), }; describe('AppController', () => { let appController: AppController; let loggingInterceptor: typeof mockLoggingInterceptor; beforeEach(async () => { const module: TestingModule = await Test.createTestingModule({ controllers: [AppController], providers: [ AppService, { provide: APP_INTERCEPTOR, useClass: LoggingInterceptor, }, ], }) .overrideProvider(APP_INTERCEPTOR) .useValue(mockLoggingInterceptor) .compile(); appController = module.get(AppController); loggingInterceptor = module.get(APP_INTERCEPTOR); }); it('调用getHello时应触发拦截器', async () => { await appController.getHello(); expect(loggingInterceptor.intercept).toHaveBeenCalled(); }); });
注意事项
- 确保mock的
intercept方法调用next.handle(),否则会中断请求处理流程 - 单元测试适合快速验证拦截器绑定,e2e测试更贴近真实运行场景
- 如果拦截器依赖其他服务,需要在测试模块中提供对应的模拟服务
内容的提问来源于stack exchange,提问作者Derek
相关产品推荐
相关产品推荐

