如何在NestJS测试中覆写认证中间件?
NestJS E2E测试中覆写全局认证中间件的实现
问题描述
我在NestJS应用中有一个E2E测试,需要测试一个受认证中间件保护的路径。现有测试代码如下:
import { Test, TestingModule } from '@nestjs/testing'; import { AppModule } from '../src/app.module'; import { INestApplication } from '@nestjs/common'; import * as request from 'supertest'; fdescribe('App health (e2e)', () => { let app: INestApplication; beforeEach(async () => { const moduleFixture: TestingModule = await Test.createTestingModule({ imports: [AppModule], }).compile(); app = moduleFixture.createNestApplication(); await app.init(); }); // override authentication middleware should come here it('should fetch a doc which is not public', () => { return request(app.getHttpServer()) .get('/some/protected/path') .expect(200) }); afterAll(()=>{ app.close() }) });
认证中间件通过应用中的PagesModule全局启用,代码如下:
export class PagesModule { configure(consumer: MiddlewareConsumer): void { consumer .apply(UserMiddleware) .forRoutes({ path: '*', method: RequestMethod.ALL }); } }
请问是否可以覆写并替换该认证中间件?能否仅为单个测试进行替换?
解决方案
完全可以覆写并替换全局认证中间件,也支持仅为单个测试做替换,具体有两种常用实现方式:
方式一:通过测试模块overrideProvider替换中间件
如果UserMiddleware是依赖注入到应用中的,可直接在测试模块中替换它的实现,让中间件跳过认证逻辑:
// 定义模拟中间件,重写认证逻辑 class MockUserMiddleware extends UserMiddleware { use(req: Request, res: Response, next: NextFunction) { // 模拟已认证用户信息,挂载到请求对象上 req.user = { id: 'test-user-id', roles: ['admin'] }; next(); } } beforeEach(async () => { const moduleFixture: TestingModule = await Test.createTestingModule({ imports: [AppModule], }) .overrideProvider(UserMiddleware) .useClass(MockUserMiddleware) .compile(); app = moduleFixture.createNestApplication(); await app.init(); });
方式二:手动修改应用中间件栈
如果中间件是直接在configure方法中实例化的(未通过DI注入),可在应用初始化后手动修改中间件栈:
beforeEach(async () => { const moduleFixture: TestingModule = await Test.createTestingModule({ imports: [AppModule], }).compile(); app = moduleFixture.createNestApplication(); await app.init(); // 获取HTTP适配器的中间件栈 const httpAdapter = app.getHttpAdapter(); const middlewareStack = httpAdapter.getMiddlewareStack(); // 遍历栈并替换目标中间件 for (let i = 0; i < middlewareStack.length; i++) { if (middlewareStack[i].handle instanceof UserMiddleware) { middlewareStack[i].handle = new MockUserMiddleware(); break; } } });
仅为单个测试替换中间件
如果只想给特定测试单独替换中间件,可拆分应用初始化逻辑,在目标测试中使用带模拟中间件的初始化方法:
fdescribe('App health (e2e)', () => { let app: INestApplication; // 通用初始化:使用原中间件 async function initApp() { const moduleFixture = await Test.createTestingModule({ imports: [AppModule], }).compile(); const appInstance = moduleFixture.createNestApplication(); await appInstance.init(); return appInstance; } // 带模拟中间件的初始化 async function initAppWithMockMiddleware() { const moduleFixture = await Test.createTestingModule({ imports: [AppModule], }) .overrideProvider(UserMiddleware) .useClass(MockUserMiddleware) .compile(); const appInstance = moduleFixture.createNestApplication(); await appInstance.init(); return appInstance; } afterEach(async () => { if (app) await app.close(); }); it('should fetch a doc which is not public', async () => { app = await initAppWithMockMiddleware(); return request(app.getHttpServer()) .get('/some/protected/path') .expect(200); }); it('should reject unauthenticated request', async () => { app = await initApp(); return request(app.getHttpServer()) .get('/some/protected/path') .expect(401); }); });
注意:若UserMiddleware未通过依赖注入(直接在configure中apply(UserMiddleware)),方式一将不生效,此时优先使用方式二修改中间件栈。
内容的提问来源于stack exchange,提问作者Yaron
相关产品推荐
相关产品推荐

