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

在测试外重写NestJS Service时实例化异常问题排查

NestJS:替换Provider却同时实例化原类与Mock类的问题

我编写了如下NestJS代码:

import { Module, Injectable } from '@nestjs/common'

@Injectable()
export class PrismaService {
  constructor() {
    console.log('init original')
  }
}

@Injectable()
export class PrismaServiceMock {
  constructor() {
    console.log('init mock')
  }
}

@Module({
  providers: [
    {
      provide: PrismaService,
      useClass: PrismaService,
    },
  ],
  exports: [PrismaService],
})
export class PrismaModule {}

@Module({
  imports: [PrismaModule],
  providers: [
    {
      provide: PrismaService,
      useClass: PrismaServiceMock,
    },
  ],
})
export class AppModule {}

我期望通过AppModule中的配置仅实例化PrismaServiceMock,但实际PrismaService和PrismaServiceMock都被创建了,请问问题出在哪?


问题原因

核心问题是PrismaModule内部已经明确声明并实例化了原PrismaService。当AppModule导入PrismaModule时,NestJS会先初始化导入模块(PrismaModule)的所有Providers,所以原PrismaService会被创建。而AppModule里的Provider配置只是在自身模块作用域内覆盖了PrismaService的实现,但无法回溯修改已导入模块的初始化逻辑。

解决方法

方法一:让PrismaModule支持动态配置

修改PrismaModule,通过静态方法接收配置选项,动态决定使用原服务还是Mock服务:

import { Module, Injectable } from '@nestjs/common'

@Injectable()
export class PrismaService {
  constructor() {
    console.log('init original')
  }
}

@Injectable()
export class PrismaServiceMock {
  constructor() {
    console.log('init mock')
  }
}

@Module({})
export class PrismaModule {
  static forRoot(options: { useMock?: boolean } = {}) {
    const prismaProvider = options.useMock 
      ? { provide: PrismaService, useClass: PrismaServiceMock }
      : { provide: PrismaService, useClass: PrismaService };
    
    return {
      module: PrismaModule,
      providers: [prismaProvider],
      exports: [PrismaService],
    };
  }
}

@Module({
  imports: [PrismaModule.forRoot({ useMock: true })],
})
export class AppModule {}

这样AppModule导入时通过forRoot传入useMock: true,PrismaModule就只会实例化Mock类,原类不会被创建。

方法二:测试环境下使用overrideProvider(仅适用于测试场景)

如果只是在单元/集成测试中需要替换服务,不需要修改模块代码,直接在测试用例中用overrideProvider覆盖:

import { Test } from '@nestjs/testing';
import { AppModule } from './app.module';
import { PrismaService } from './prisma.service';
import { PrismaServiceMock } from './prisma.service.mock';

describe('AppModule', () => {
  it('should use PrismaServiceMock', async () => {
    const moduleFixture = await Test.createTestingModule({
      imports: [AppModule],
    })
    .overrideProvider(PrismaService)
    .useClass(PrismaServiceMock)
    .compile();

    const prismaService = moduleFixture.get(PrismaService);
    expect(prismaService).toBeInstanceOf(PrismaServiceMock);
  });
});

这种方式只会在测试环境中替换服务,不影响开发/生产环境的模块初始化逻辑。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.31 04:01:56