You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 15:07:13