NestJS Jest单元测试:自定义Mock实现未被控制器调用
问题:NestJS Jest测试中使用VpcServiceMock替代真实服务报错
TypeError: this.vpcService.list is not a function 我已实现VpcServiceMock类,希望在NestJS的Jest单元测试中用其替代真实VpcService,但运行测试时报错:TypeError: this.vpcService.list is not a function。
相关代码
控制器代码
export class VpcController { constructor(private readonly vpcService: VpcService) {} @Get() list() { return this.vpcService.list(); } }
测试代码
class VpcServiceMockFactory { private list() { return jest.fn().mockResolvedValue([TEMPLATE_VPC]); } getMock() { const repoService: RepositoryService = new RepositoryService(); const vpsServiceMock: VpcServiceMock = new VpcServiceMock(repoService); vpsServiceMock.create(TEMPLATE_VPC.name); // 此写法生效 // return { // list: this.list(), // } // 此写法报错 return { list: vpsServiceMock.list(), get: vpsServiceMock.get(TEMPLATE_VPC.id), // create: vpsServiceMock.create('new-vpc'), // update: vpsServiceMock.update(TEMPLATE_VPC.id, 'updated-name'), // delete: vpsServiceMock.delete(TEMPLATE_VPC.id), } } } describe('VpcControllerTest', () => { let controller: VpcController; let spyService: VpcService; beforeEach(async () => { // Mock Services const MockVpcServiceProvider = { provide: VpcService, useFactory: () => new VpcServiceMockFactory().getMock() } // 待测试类实例化 const module: TestingModule = await Test.createTestingModule({ controllers: [VpcController], providers: [VpcService, MockVpcServiceProvider], }).compile() // 获取实例 controller = module.get<VpcController>(VpcController); spyService = module.get<VpcService>(VpcService); }); it('Get collection of VPCs', async () => { // 执行方法 const result = await controller.list(); // 断言 expect(spyService.list).toHaveBeenCalled(); expect(result.length).toBe(1); expect(result[0].name).toBe('zopa'); }); });
VpcServiceMock类代码
export class VpcServiceMock { constructor(private repository: RepositoryService) {} list() { return this.repository.list<VpcModel>(VPC); } }
RepositoryService类的list方法
async list<T>(type: VPC): Promise<T[]> { return <T[]>this.aSimpleJsonObject[type]; }
问题原因
- 函数调用时机错误:在
getMock方法中,你直接执行了vpsServiceMock.list()并将返回值(一个Promise对象)赋值给list属性。当控制器调用this.vpcService.list()时,实际是试图调用一个Promise而非函数,因此触发is not a function错误。 - 生效写法的差异:之前生效的
list: this.list()中,this.list()返回的是Jest mock函数(jest.fn()),属于函数类型,所以控制器调用时能正常执行。
解决方案
方式一:直接返回Mock类实例并Spy方法(推荐)
修改VpcServiceMockFactory.getMock,直接返回VpcServiceMock实例,同时用Jest spy方法以便后续断言:
getMock() { const repoService: RepositoryService = new RepositoryService(); const vpsServiceMock: VpcServiceMock = new VpcServiceMock(repoService); vpsServiceMock.create(TEMPLATE_VPC.name); // Spy mock类的方法,保留原有逻辑同时支持调用断言 jest.spyOn(vpsServiceMock, 'list'); // 如果需要自定义get方法的返回值,可以添加: // jest.spyOn(vpsServiceMock, 'get').mockResolvedValue(TEMPLATE_VPC); return vpsServiceMock; // 直接返回mock实例 }
同时调整测试模块的providers配置,移除重复的VpcService(否则Nest会优先注入真实服务):
const module: TestingModule = await Test.createTestingModule({ controllers: [VpcController], providers: [ { provide: VpcService, useFactory: () => new VpcServiceMockFactory().getMock() } ], }).compile();
方式二:手动包裹方法为函数
如果坚持手动构造返回对象,需要将方法包裹成箭头函数,避免直接执行:
return { list: () => vpsServiceMock.list(), // 用箭头函数包裹,返回原方法执行结果 get: (id: string) => vpsServiceMock.get(id), // 其他方法同理 };
注意:这种方式无法直接用expect(spyService.list).toHaveBeenCalled()断言,需要额外处理,因此优先推荐方式一。
内容的提问来源于stack exchange,提问作者Pradip
相关产品推荐
相关产品推荐

