TypeScript์์๋ ์ ๋๊ฒฝ๋ก๋ฅผ ์ดํดํ ์ ์์ง๋ง jest๋ ์๋์ ๊ฐ์ด ๊ฒฝ๋ก๋ฅผ ์ดํดํ์ง ๋ชปํ๋ค.
Cannot find module 'src/common/entities/core.entity' from 'users/entities/user.entity.ts'
// package.json
"jest": {
"moduleNameMapper": {
"^src/(.*)$": "<rootDir>/$1"
},
"moduleDirectories": [
"src",
"node_modules"
],
}
์ ๋ ํ ์คํธ์์ ์ผ๋ฐ์ ์ผ๋ก ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ฐ๊ฒฐ์ ํผํ๊ณ ํ ์คํธ๋ฅผ ๋ ๋ฆฝ์ ์ผ๋ก ์ ์งํ๊ณ ์คํ ํ๋ก์ธ์ค๋ฅผ ์ต๋ํ ๋น ๋ฅด๊ฒ ์ ์งํ๊ธฐ๋ฅผ ์ํ๋ค.
์ด๋ฅผ ์ํด์๋ ์ฐ๊ฒฐ ์ธ์คํด์ค์์ ๊ฐ์ ธ์จ Repository ๋์ ์ Mock Repository๋ฅผ ๋ง๋ค์ด custom providers๋ฅผ ์ฌ์ฉํ ์ ์๋ค.
ํด๋์ค๊ฐ @InjectRepository() ๋ฐ์ฝ๋ ์ดํฐ๋ฅผ ์ฌ์ฉํ์ฌ UsersRepository๋ฅผ ์์ฒญํ ๋๋ง๋ค Nest๋ ๋ฑ๋ก๋ mockRepository ๊ฐ์ฒด๋ฅผ ๋์ ์ฌ์ฉํ๋ค.
https://docs.nestjs.com/techniques/database#testing
describe('UserService', () => {
let service: UserService;
let usersRepository: MockRepository<User>;
beforeAll(async () => {
const module = await Test.createTestingModule({
providers: [
UserService,
{
provide: getRepositoryToken(User),
useValue: mockRepository,
},
{
provide: getRepositoryToken(Verification),
useValue: mockRepository,
},
{
provide: JwtService,
useValue: mockJwtService,
},
{
provide: MailService,
useValue: mockMailService,
},
],
}).compile();
service = module.get<UserService>(UserService);
usersRepository = module.get(getRepositoryToken(User));
});
it('should fail if the password is wrong', async () => {
const mockedUser = {
checkPassword: jest.fn(() => Promise.resolve(false)),
};
usersRepository.findOne.mockResolvedValue(mockedUser);
const result = await service.login(loginArgs);
expect(result).toEqual({ ok: false, error: 'Wrong Password' });
});
jest.fn(implements)const mockRepository = {
findOne: jest.fn(),
save: jest.fn(),
create: jest.fn(),
};
const mockJwtService = {
sign: jest.fn(),
verify: jest.fn(),
};
const mockMailService = {
sendVerificationEmail: jest.fn(),
};
ํ
์คํธ๋ฅผ ํ ๋ ๋ ํฌ์งํ ๋ฆฌ๋ฅผ ๋ฐ์์์ ์ฌ์ฉํ์ง ์๊ณ mock Repository๋ฅผ ์ฌ์ฉํด์ผ ํ๋๋ฐ ์ด ๋ mock Repository์ ์๋ ๋ ํฌ์งํ ๋ฆฌ๋ฅผ ๋ฐ์์์ ์ฌ์ฉํ ๋์ ๊ฐ์ด Repository ๋ด์ฅ ํจ์๋ฅผ ๋ชจ๋ ์ฌ์ฉํ ์ ์์ด์ผ๋ง ํ๋ค. ์ด ๋ด์ฅ ํจ์ ๋ํ ๋ชจ๋ mock type์ผ๋ก ๋ง๋ค๋ฉฐ ๊ทธ ๋ ์ฌ์ฉํ๋ ๊ฒ์ด Record์ด๋ค.
Record๋ ์์ฑ ํค๊ฐ Key์ด๊ณ ์์ฑ ๊ฐ์ด Type์ธ ๊ฐ์ฒด ์ ํ์ ๊ตฌ์ฑํ๋ค.
์ด ์ ํธ๋ฆฌํฐ๋ ์ ํ์ ์์ฑ์ ๋ค๋ฅธ ์ ํ์ ๋งคํํ๋ ๋ฐ ์ฌ์ฉํ ์ ์๋ค.
type MockRepository<T = any> = Partial<Record<keyof Repository<T>, jest.Mock>>;
์๋์ ์ฝ๋์ ๊ฐ์ด ํ
์คํธํ๊ณ ์ ํ๋ ํจ์(createAccount)๋ฅผ ์คํํ๊ธฐ ์ ์ mockfn.mockResolvedValue๋ฅผ ์ฌ์ฉํ๋ฉด userService์ createAccount ์คํ์ ์ฐ์ด๋ findOne line์ ๊ฐ๋ก์ฑ mock result๋ฅผ return ํ ์ ์๋ค.
์ด๋ ๊ฒ ํจ์ผ๋ก์จ ๋ค๋ฅธ function๊ณผ ์ค์ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์์กดํ์ง ์๊ณ ํจ์ ์์ฒด๋ง์ ํ ์คํธํ ์ ์๋ค. ์ด๋ ๊ฒ ํ๋ ์ด์ ๋ integration test๊ฐ ์๋ unit test, ํจ์ ํ๋๋ง์ ํ ์คํธํ๊ธฐ ์ํ ๊ฒ์ด๊ธฐ ๋๋ฌธ์ด๋ค.
describe('createAccount', () => {
it('Should fail if user exists', async () => {
usersRepository.findOne.mockResolvedValue({
id: 1,
email: 'exist@naver.com',
});
const result = await service.createAccount({
email: '',
password: '',
role: 0,
});
expect(result).toMatchObject({
ok: false,
error: 'There is a user with that email already',
});
});
});
npm run test:covnpm run test:cov๋ ํ
์คํธ์ ์งํ์ํฉ(์ด๋ ํ ํ์ผ์ ๋ช ํผ์ผํธ๊ฐ ํ
์คํธ ๋์๊ณ ๋ช ๋ฒ์งธ ์ค์ด ํ
์คํธ ๋์๋์ง)๋ฅผ ๋ํ๋ธ๋ค. ์คํํ๊ฒ ๋๋ฉด ์์น ์๋ ํ์ผ์ ์งํ์ํฉ๊น์ง ๋ชจ๋ ๋ณด์ฌ์ฃผ๊ธฐ ๋๋ฌธ์ ์ด๋ฅผ ์ ํํ๊ธฐ ์ํด์๋ package.json์ ๋ค์๊ณผ ๊ฐ์ด ์์ ํด์ผํ๋ค.
"coveragePathIgnorePatterns": [
"node_modules",
".entity.ts",
".constants.ts"
]
usersRepository.findOne.mockResolvedValue(undefined);
usersRepository.create.mockReturnValue(createAccountArgs);
https://github.com/Ju-yeop/nuber-eats-backend/blob/main/src/users/users.service.spec.ts