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

如何在Jest端到端测试中使用jest.spyOn覆盖jest.mock模块中的模拟方法?

如何在Jest端到端测试中使用jest.spyOn覆盖jest.mock模块中的模拟方法?

嘿,这个问题我之前碰到过,咱们来一步步拆解原因和解决办法~

首先得搞清楚为什么单元测试和e2e测试的表现不一样:
在单元测试里,你是直接把mock的PrismaService注入到FooService中,依赖关系简单直接,你spyOn的实例和业务代码使用的是同一个,所以覆盖能正常生效。但到了e2e测试,因为要启动完整的Nest应用,情况就变复杂了:

可能的原因

  • 实例引用不匹配:如果你的FooService在构造函数里缓存了prisma.foo这类子对象的引用(比如this.fooRepo = this.prisma.foo),那后续你spyOnprismaService.foo.findUnique时,FooService用的还是之前缓存的旧对象,新的mock自然不会生效。
  • DI容器的实例创建时机:当你通过Test.createTestingModule导入AppModule时,Nest会在模块编译阶段创建所有单例服务的实例。如果你的mock替换或spyOn的时机不对,就可能没覆盖到实际被使用的实例。
  • 拼写错误:看你的代码,beforeEach里写的是prismaServie = app.get(...)(少了一个字母c),但测试用例里用的是prismaService——如果这是实际代码里的错误,那你的spyOn根本没作用到正确的实例上,测试肯定会失败!

解决办法

办法一:避免缓存依赖子对象的引用

检查你的FooService代码,不要直接缓存prisma.foo这类子对象,而是每次都通过this.prisma.foo调用方法:

// 不推荐的写法(缓存了子对象引用)
@Injectable()
export class FooService {
  private fooRepo;
  constructor(private prisma: PrismaService) {
    this.fooRepo = prisma.foo;
  }

  async findOne(id: number) {
    return this.fooRepo.findUnique({ where: { id } });
  }
}

// 推荐的写法
@Injectable()
export class FooService {
  constructor(private prisma: PrismaService) {}

  async findOne(id: number) {
    return this.prisma.foo.findUnique({ where: { id } });
  }
}

这样你spyOnprismaService.foo.findUnique时,FooService调用的就是被覆盖后的方法了。

办法二:直接修改mock函数的实现

既然你已经用jest.mock替换了PrismaService,可以直接拿到实例后修改mock函数的返回值,比spyOn更直接可靠:

it('', async () => {
  // 直接修改mock函数的返回值
  prismaService.foo.findUnique.mockResolvedValue({ name: 'foo-spy' });

  return request(app.getHttpServer())
    .get('/foo/123')
    .expect(({ body }) => {
      expect(body).toEqual({ name: 'foo-spy' })
    });
});

办法三:在测试模块中手动提供mock实例

如果你不想修改业务代码,可以在创建测试模块时,手动替换掉PrismaService的提供者,确保你能完全控制实例:

beforeEach(async () => {
  // 创建一个可控制的mock实例
  const mockPrisma = new PrismaService();
  const module = await Test.createTestingModule({
    imports: [AppModule],
    providers: [
      {
        provide: PrismaService,
        useValue: mockPrisma,
      },
    ],
  }).compile();

  app = module.createNestApplication();
  await app.init();

  prismaService = mockPrisma; // 直接使用我们创建的mock实例
});

这样不管是用spyOn还是直接修改mock函数,都能确保作用到实际被应用使用的实例上。

总的来说,核心就是要保证你修改的mock函数,和业务代码实际调用的是同一个实例上的方法。上面的几种办法都能解决问题,你可以根据自己的代码结构选最合适的。

备注:内容来源于stack exchange,提问作者Joe

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.14 17:13:01