Node, Nest, Deno/🦁 Nest - Series
Nest + Jest unit test (4) 외부 패키지 mocking
DarrenKwonDev
2020. 12. 12. 19:12
아래 service 코드를 테스트해보기로 하였다. 여기서 문제는 jsonwebtoken라는 외부 패키지를 사용했다는 것이다.
/* eslint-disable @typescript-eslint/ban-types */
import { Inject, Injectable } from '@nestjs/common';
import { JWTModuleOptions } from './jwt.interfaces';
import jwt from 'jsonwebtoken';
import { CONFIG_OPTIONS } from 'src/common/common.constants';
@Injectable()
export class JwtService {
constructor(@Inject(CONFIG_OPTIONS) private readonly options: JWTModuleOptions) {}
sign(payload: object): string {
// jwt 생성을 위한 private key는 이미 jwt module 단위에서 provide 해줬음
return jwt.sign(payload, this.options.privateKey);
}
verify(token: string) {
return jwt.verify(token, this.options.privateKey);
}
}
아래와 같이 테스트를 만든다면 비즈니스 로직에 따라 해쉬화된 문자열을 반환하기는 하지만 실제 jsonwebtoken 패키지를 사용하게 되는 것입니다. 하지만 이미지 만들어진 패키지를 테스트, 검증하는 것이 의미가 있을까요?
아래 코드를 테스트하고 싶다면, 올바르게 패키지를 사용했는가를 체킹 해야 합니다.
즉, 몇 번 호출이 되었는지, 인자는 제대로 넣었는 지에 대한 테스트가 진행되어야 합니다.
describe('JwtService', () => {
let service: JwtService;
beforeEach(async () => {
const module = await Test.createTestingModule({
providers: [
JwtService,
{
provide: CONFIG_OPTIONS,
useValue: { privateKey: TEST_KEY },
},
],
}).compile();
service = module.get<JwtService>(JwtService);
});
describe('sign', () => {
it('should return a signed token', () => {
const token = service.sign({ id: 1 }); // jsonwebtoken 패키지 로직을 테스트하는 꼴이 됨
console.log(token);
});
});
});
따라서 jsonwebtoken 메서드를 사용하는 방법을 테스트해야 하고, 이를 위해 import한 패키지 또한 mocking이 가능합니다. 아래와 같습니다.
import jwt from 'jsonwebtoken'; // test 코드에 직접 import
// jwt 패키지의 기본 메서드 결과물도 mocking이 가능
jest.mock('jsonwebtoken', () => {
return {
sign: jest.fn(() => 'TOKEN'),
verify: jest.fn(() => 'verify'),
};
});
describe('sign', () => {
it('should return a signed token', () => {
const ID = 1;
const token = service.sign({ id: ID }); // service 내부에 jwt.sign 메서드가 실행됨
// jwt.sign이 1번 실행되고, 들어가는 인자만 테스트하는 코드.
// 결과값은 mocking하였음
expect(jwt.sign).toHaveBeenCalledTimes(1);
expect(jwt.sign).toHaveBeenCalledWith({ id: ID }, TEST_KEY);
expect(token).toBe('TOKEN');
});
});
일반 typeorm entity method를 모킹할 때와 다릅니다.
// typeorm entity method 모킹
const mockRepository = () => ({
findOne: jest.fn(),
save: jest.fn(),
create: jest.fn(),
findOneOrFail: jest.fn(),
delete: jest.fn(),
});
// jwt 모킹
import jwt from 'jsonwebtoken';
jest.mock('jsonwebtoken', () => {
return {
sign: jest.fn(() => 'TOKEN'),
verify: jest.fn(() => ({ id: USER_ID })),
};
});
특정 패키지의 특정 메서드만 mocking하는 것이 아니라 패키지 전체를 모킹할 수도 있습니다.
jest.mock('got');