NestJS中Jest单元测试报错:超出子进程异常重试限制
问题分析与解决方案
核心问题
你的测试代码存在三个关键问题,直接导致Jest worker崩溃和断言失败:
- 异步操作时序错误:调用
usersService.update后未等待异步逻辑完成就执行断言,同时未捕获的异常直接终止worker进程 - 调用参数断言语法错误:错误使用
toHaveBeenCalledWith传入调用顺序索引,应该用toHaveBeenNthCalledWith验证指定次数的调用参数 - 缺失依赖Mock:测试模块未提供
Verification仓库的Mock,Service执行时调用该仓库方法会抛出未定义错误,进一步触发异常
修复步骤
1. 补充Verification仓库的Mock
在测试模块的providers中添加Verification仓库的Mock,并初始化对应的测试变量:
// 在beforeEach的createTestingModule中新增 { provide: getRepositoryToken(Verification), useValue: mockRepository(), } // 同时在测试变量声明中添加 let verificationsRepository: MockRepository<Verification>; // 在beforeEach中初始化 verificationsRepository = module.get(getRepositoryToken(Verification));
2. 修正异步操作与断言顺序
将断言调整到异步操作完成后执行,确保时序正确:先验证异常,再验证调用次数和参数。
3. 修正调用参数断言语法
将toHaveBeenCalledWith替换为toHaveBeenNthCalledWith,指定第N次调用的参数:
expect(usersRepository.findOneBy).toHaveBeenNthCalledWith(1, { id }); expect(usersRepository.findOneBy).toHaveBeenNthCalledWith(2, { id: Not(id), email: newUser.email, });
4. Mock Verification仓库的方法
为避免Service执行到verificationsRepository.create和save时出错,添加mock返回值:
verificationsRepository.create.mockReturnValue({ code: 'test-code' }); verificationsRepository.save.mockResolvedValue({ code: 'test-code' });
修改后的完整测试代码
// users.service.spec.ts import { Test } from '@nestjs/testing'; import { UsersService } from './users.service'; import { getRepositoryToken } from '@nestjs/typeorm'; import { User } from './entities/user.entity'; import { Verification } from './entities/verification.entity'; import { MailerService } from '@nestjs-modules/mailer'; import { Not, Repository } from 'typeorm'; import { HttpException, HttpStatus } from '@nestjs/common'; type MockRepository<T> = Partial<Record<keyof Repository<T>, jest.Mock>>; const mockRepository = () => ({ create: jest.fn(), delete: jest.fn(), findOne: jest.fn(), findOneBy: jest.fn(), save: jest.fn(), }); const mockMailerService = () => ({ sendMail: jest.fn(), }); // Start test describe('UsersService', () => { let usersService: UsersService; let mailerService: MailerService; let usersRepository: MockRepository<User>; let verificationsRepository: MockRepository<Verification>; beforeEach(async () => { const module = await Test.createTestingModule({ providers: [ UsersService, { provide: getRepositoryToken(User), useValue: mockRepository(), }, { provide: getRepositoryToken(Verification), useValue: mockRepository(), }, { provide: MailerService, useValue: mockMailerService(), }, ], }).compile(); usersService = module.get<UsersService>(UsersService); mailerService = module.get<MailerService>(MailerService); usersRepository = module.get(getRepositoryToken(User)); verificationsRepository = module.get(getRepositoryToken(Verification)); }); it('should be defined', () => { expect(usersService).toBeDefined(); }); describe('update', () => { const id = 1; const oldUser = { email: 'olduser@gmail.com', isVerified: true }; const newUser = { email: 'newuser@gmail.com', password: 'test1234' }; it('should fail if the user using the email already exists', async () => { // Mock仓库方法返回值 usersRepository.findOneBy .mockResolvedValueOnce(oldUser) .mockResolvedValueOnce(newUser); verificationsRepository.create.mockReturnValue({ code: 'test-code' }); verificationsRepository.save.mockResolvedValue({ code: 'test-code' }); // 获取异步操作的Promise const updatePromise = usersService.update(id, newUser); // 先验证异常 await expect(updatePromise).rejects.toEqual( new HttpException('The email is already taken', HttpStatus.BAD_REQUEST), ); // 再验证调用次数和参数 expect(usersRepository.findOneBy).toHaveBeenCalledTimes(2); expect(usersRepository.findOneBy).toHaveBeenNthCalledWith(1, { id }); expect(usersRepository.findOneBy).toHaveBeenNthCalledWith(2, { id: Not(id), email: newUser.email, }); }); }); });
额外说明
- 测试异步方法时,必须确保所有异步操作完成后再执行断言,否则会出现时序问题导致断言失败或未捕获异常
- Service依赖的所有外部资源(数据库仓库、第三方服务)都需要在测试中提供Mock,避免真实调用引发错误
Jest worker encountered 4 child process exceptions错误通常由未捕获的异步异常导致worker崩溃,解决底层异常后该错误会自动消失
内容的提问来源于stack exchange,提问作者Jupyo Hong
相关产品推荐
相关产品推荐

