I’m encountering issues with running Jest test in my NestJS project. I’ve followed the setup guides and examples provided, but my test is failing to execute properly. Here’s breakdown of my setup and the problem I’m facing:
Problem:
When running Jest test for “updating user account” unit test, the test fails to throw an error if the user wants to change the email which is already in used. I’ve ensured that all dependencies are correctly installed, and I’ve followed the recommended configuration for Jest testing with NestJS. The error occurred as below:
/src/users/users.service.ts:86
throw new common_1.HttpException('The email is already taken', common_1.HttpStatus.BAD_REQUEST);
^
HttpException: The email is already taken
at UsersService.update (/src/users/users.service.ts:86:23)
at processTicksAndRejections (node:internal/process/task_queues:95:5) {
response: 'The email is already taken',
status: 400,
options: undefined
}
Node.js v18.16.0
FAIL src/users/users.service.spec.ts
● Test suite failed to run
Jest worker encountered 4 child process exceptions, exceeding retry limit
at ChildProcessWorker.initialize (../node_modules/jest-worker/build/workers/ChildProcessWorker.js:181:21)
Test Suites: 1 failed, 1 total
Tests: 0 total
Snapshots: 0 total
Time: 8.608 s
Ran all test suites related to changed files.
Watch Usage: Press w to show more.
Relevant Code:
Here’s a part of my UsersService
:
// users.service.ts
@Injectable()
export class UsersService {
constructor(
@InjectRepository(User) private readonly usersRepository: Repository<User>,
@InjectRepository(Verification)
private readonly verificationsRepository: Repository<Verification>,
private readonly mailerService: MailerService,
) {}
async update(id: number, updateUserInput: UpdateUserInput): Promise<User> {
const user = await this.usersRepository.findOneBy({ id });
if (!user) {
throw new HttpException('User not found', HttpStatus.NOT_FOUND);
}
if (updateUserInput.email) {
// check if the email is already used
const duplicatedEmailUser = await this.usersRepository.findOneBy({
id: Not(id),
email: updateUserInput.email,
});
if (duplicatedEmailUser) {
throw new HttpException(
'The email is already taken',
HttpStatus.BAD_REQUEST,
);
}
// set new values
user.email = updateUserInput.email;
user.isVerified = false;
// create verification code
const verification = await this.verificationsRepository.save(
this.verificationsRepository.create({ user }),
);
// send verification code through email
this.mailerService.sendMail({
to: user.email,
from: 'please-no-reply <"[email protected]">',
subject: '[NestJS Test] Verification code for your account',
html: `<p>Your verification code: ${verification.code}</p>`,
});
}
return await this.usersRepository.save(user);
}
}
And here’s my 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>;
// https://jestjs.io/docs/setup-teardown#repeating-setup
beforeEach(async () => {
const module = await Test.createTestingModule({
providers: [
UsersService,
{
provide: getRepositoryToken(User),
useValue: mockRepository(),
},
{
provide: MailerService,
useValue: mockMailerService(),
},
],
}).compile();
usersService = module.get<UsersService>(UsersService);
mailerService = module.get<MailerService>(MailerService);
usersRepository = module.get(getRepositoryToken(User));
});
it('should be defined', () => {
expect(usersService).toBeDefined();
});
describe('update', () => {
const id = 1;
const oldUser = { email: '[email protected]', isVerified: true };
const newUser = { email: '[email protected]', password: 'test1234' };
it('should fail if the user using the email already exists', async () => {
// due to `findOneBy` is used twice in usersService, so I mock resolved value twice separately.
usersRepository.findOneBy
.mockResolvedValueOnce(oldUser)
.mockResolvedValueOnce(newUser);
const result = usersService.update(id, newUser);
expect(usersRepository.findOneBy).toHaveBeenCalledTimes(2);
expect(usersRepository.findOneBy).toHaveBeenCalledWith(1, { id });
expect(usersRepository.findOneBy).toHaveBeenCalledWith(2, {
id: Not(id),
email: newUser.email,
});
await expect(result).rejects.toEqual(
new HttpException('The email is already taken', HttpStatus.BAD_REQUEST),
);
});
});
});
What I’ve Tried
- Add
--maxWorkers
option when runningjest --maxWorkers 1 --watch
to see the real error, but still get the same one. - Append and remove
await
keyword where it is required.
Environment:
- NestJS version: 10.3.2
- Jest version: 29.5.12
- Node.js version: 18.16.0
- Operating System: macOS Sonoma 14.3.1 (Apple M1)
Any assistance in resolving this issue would be greatly appreciated. Thank you in advance for your help!