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

NestJS多模块复用控制器Jest测试依赖注入冲突问题

NestJS测试中依赖注入令牌冲突问题解决

问题场景

开发时定义了继承自MyAbstractService的三个服务ServiceA/ServiceB/ServiceC,同时三个模块ModuleA/ModuleB/ModuleC复用同一个MyController,每个模块都以COMMON_SERVICE为令牌注入对应服务。生产环境启动应用时注入符合预期,但Jest测试中通过Test.createTestingModule导入MainModule并select(ModuleB)获取控制器时,注入的却是ServiceC而非ServiceB,尝试useFactory也未解决。

核心原因

Nest的DI容器在测试环境中,当多个模块注册同一个令牌(COMMON_SERVICE)时,后注册的provider会覆盖之前的。由于MainModule按顺序导入ModuleA→ModuleB→ModuleC,ServiceC的provider最后被注册到全局容器。同时MyController默认是单例作用域,多个模块复用同一个控制器类时,最终只会存在一个控制器实例,该实例会使用全局容器中最后注册的COMMON_SERVICE实现。

解决方案

方案1:给控制器添加模块级作用域

为MyController添加@Scope('module')装饰器,让每个模块拥有独立的控制器实例,确保每个模块的控制器注入对应模块的服务:

import { Controller, Inject, Scope } from '@nestjs/common';

@Controller()
@Scope('module') // 添加模块级作用域
export class MyController {
  constructor(
    @Inject("COMMON_SERVICE") private readonly commonService: MyAbstractService,
  ) {
    commonService.doSomething();
  }
}

注:也可根据业务需求选择@RequestScope()(请求级实例)或@Transient()(每次获取新实例),核心是打破单例复用。

方案2:每个模块使用独立控制器子类

避免复用同一个控制器类,为每个模块创建控制器子类,确保每个模块的控制器属于自身模块上下文:

// 基类控制器(提取通用逻辑)
export class BaseMyController {
  constructor(
    @Inject("COMMON_SERVICE") private readonly commonService: MyAbstractService,
  ) {
    commonService.doSomething();
  }
}

// ModuleA专属控制器
@Controller()
export class ModuleAController extends BaseMyController {}

// ModuleB专属控制器
@Controller()
export class ModuleBController extends BaseMyController {}

// ModuleC专属控制器
@Controller()
export class ModuleCController extends BaseMyController {}

修改各模块配置,替换为对应专属控制器:

@Module({
  controllers: [ModuleAController],
  providers: [
    {
      provide: "COMMON_SERVICE",
      useClass: ServiceA,
    },
  ]
})
export class ModuleA {}

// ModuleB、ModuleC同理替换控制器

测试时直接获取目标模块的专属控制器:

const controller = module
  .select(ModuleB)
  .get<ModuleBController>(ModuleBController);

方案3:测试时单独导入目标模块

不需要导入整个MainModule,只导入需要测试的ModuleB,彻底避免其他模块的provider干扰:

const module = await Test.createTestingModule({
  imports: [ModuleB],
}).compile();
const controller = module.get<MyController>(MyController);

内容的提问来源于stack exchange,提问作者rnoodle

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.01 12:30:15