Jest测试报错:TypeError:service(...).returns不是函数(Nest.js+TypeORM)
Jest单元测试报错:TypeError: contractService.getContractStatusEditable(...).returns is not a function
在基于Nest.js和TypeORM开发的服务中,编写assignUsersToContracts方法的Jest单元测试时,出现上述TypeError。部分测试用例预期捕获GeneralApplicationException,实际却捕获到TypeError。
服务代码
@EnableLog() public async assignUsersToContracts( id: string, contractUsers: ContractUserInput[], authUser: any, ): Promise<string> { const contract = await this.contractService.findOneOrFail({ where: { id }, }); const statusEditable = this.contractService.getContractStatusEditable(); if (!statusEditable.includes(contract.status)) { throw new GeneralApplicationException( 'Users cannot be assigned at this stage', ); } const assignUsers: ContractUser[] = []; if (contract.assignedUsers) { for (const item of contract.assignedUsers) { await this.contractUserRepo.softDelete(item.id); } } for (const contractUser of contractUsers) { const newUser = await this.userService.getOrCreate( { email: contractUser.email }, { email: contractUser.email, firstName: contractUser.firstName, }, ); if (!newUser) throw new GeneralApplicationException( 'Not able to share to the user for now', ); assignUsers.push({ userId: newUser.id, role: contractUser.role, } as CoUser); } await this.contractUserRepo.save(assignUsers); return 'Successfull'; }
测试代码
describe('assignUsersToContracts', () => { it('should throw an error when contract is not editable ', async () => { try { contractRepo.findOne.mockResolvedValueOnce(newContract); contractService .findOneOrFail(Arg.any()) .returns(Promise.resolve(newContract)); contractService .getContractStatusEditable() .returns(await Promise.resolve(editContractStatus)); await contractUserService.assignUsersToContracts( newContract.id, contractUserInputList, authUser, ); } catch (error) { expect(error).toBeInstanceOf(GeneralApplicationException); } }); it('should throw an error when a user is undefined ', async () => { try { contractRepo.findOne.mockResolvedValueOnce(newEditableContract); contractService .findOneOrFail(Arg.any()) .returns(Promise.resolve(newEditableContract)); contractService .getContractStatusEditable() .returns(await Promise.resolve(editContractStatus)); contractUserRepository.softDelete.mockResolvedValueOnce(deleteResult); userService .getOrCreate(Arg.any(), Arg.any()) .returns(Promise.resolve(undefined)); await contractUserService.assignUsersToContracts( newEditableContract.id, contractUserInputList, authUser, ); } catch (error) { expect(error).toBeInstanceOf(GeneralApplicationException); } }); it('should save a user to a Contract', async () => { try { contractRepo.findOne.mockResolvedValueOnce(newEditableContract); contractService .findOneOrFail(Arg.any()) .returns(Promise.resolve(newEditableContract)); contractService .getContractStatusEditable() .returns(await Promise.resolve(editContractStatus)); contractUserRepository.softDelete.mockResolvedValueOnce(deleteResult); userService .getOrCreate(Arg.any(), Arg.any()) .returns(Promise.resolve(userResponse.user)); contractUserRepository.save.mockResolvedValueOnce(userResponse); const res = await contractUserService.assignUsersToContracts( newContract.id, contractUserInputList, authUser, ); expect(res).toEqual('Successfull'); } catch (error) { console.log(error); } }); });
报错信息
● Test ContractUserService › assignUsersToContracts › should throw an error when a user is undefined expect(received).toBeInstanceOf(expected) Expected constructor: GeneralApplicationException Received constructor: TypeError 315 | ); 316 | } catch (error) { > 317 | expect(error).toBeInstanceOf(GeneralApplicationException); | ^ 318 | } 319 | }); 320 | it('should save a user to a Contract', async () => { console.log TypeError: contractService.getContractStatusEditable(...).returns is not a function at Object.<anonymous> (/t/service/contract.user.service.test.ts:328:12) at processTicksAndRejections (internal/process/task_queues.js:95:5) Test Suites: 1 failed, 1 total
问题原因与修复方案
原因
getContractStatusEditable是同步方法(服务代码中直接调用,无await),但测试代码错误地使用了Jest异步mock的.returns()方法。.returns()仅适用于异步mock函数的链式调用,同步mock需直接设置返回值。
修复步骤
修正同步方法的mock写法:
替换错误的.returns()调用,改用mockReturnValue:// 错误写法 contractService.getContractStatusEditable().returns(await Promise.resolve(editContractStatus)); // 正确写法(若用spyOn) jest.spyOn(contractService, 'getContractStatusEditable').mockReturnValue(editContractStatus); // 若contractService本身是mock实例 contractService.getContractStatusEditable.mockReturnValue(editContractStatus);统一异步方法的mock规范:
findOneOrFail是异步方法,正确mock方式如下:// 错误写法 contractService.findOneOrFail(Arg.any()).returns(Promise.resolve(newContract)); // 正确写法 jest.spyOn(contractService, 'findOneOrFail').mockResolvedValue(newContract);优化错误捕获逻辑:
用expect().rejects.toThrow()替代try-catch,更简洁可靠:it('should throw an error when contract is not editable ', async () => { jest.spyOn(contractService, 'findOneOrFail').mockResolvedValue(newContract); jest.spyOn(contractService, 'getContractStatusEditable').mockReturnValue(editContractStatus); await expect(contractUserService.assignUsersToContracts( newContract.id, contractUserInputList, authUser, )).rejects.toBeInstanceOf(GeneralApplicationException); });
内容的提问来源于stack exchange,提问作者Anto P V
相关产品推荐
相关产品推荐

