@Injectable()
export class UsersService {
constructor(@InjectRepository(User) private repo: Repository<User>) {}
async rename(id: number, name: string) {
const user = await this.repo.findOneBy({ id });
if (!user) throw new NotFoundException();
user.name = name.trim();
return this.repo.save(user);
}
}
Test two behaviours: the missing-user case and the happy path. Assert results, not every call.
Setup. Create repo = { findOneBy: jest.fn(), save: jest.fn() }. Provide it in Test.createTestingModule with { provide: getRepositoryToken(User), useValue: repo }. Or just call new UsersService(repo as any), because there is nothing else to wire.
Missing user:
- Make
findOneByresolve tonull. - Assert
await expect(service.rename(1, 'x')).rejects.toThrow(NotFoundException). - Check that
savewas not called.
Happy path:
- Make
findOneByresolve to a user. - Make
savereturn its argument withmockImplementation(async (u) => u). - Assert that the result has the trimmed name.
- Assert that
savereceived that user.
Don't check exactly how findOneBy was called, unless that is the behaviour you care about. Whether the query really finds the row belongs in an integration test.
- Provide the mock with getRepositoryToken(User), or create the class directly.
- For the not-found path, await rejects.toThrow(NotFoundException) and check that save was not called.
- For the happy path, check that the returned value has the trimmed name and that save received it.
- Don't over-specify calls, because only an integration test can prove the query is correct.
Read more