如何在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
相关产品推荐
相关产品推荐

