View
배경
TDD를 학습하며 Jest의 기본 문법을 정리한다.
Jest
Facebook에서 만든 테스팅 프레임워크
"Delightful Javascript Testing"
https://jestjs.io/
시작하기
jest 설치
$ npm run i -D jest
babel 설정
$ yarn add --dev babel-jest @babel/core @babel/preset-env
// babel.config.js
module.exports = {
presets: [
[
'@babel/preset-env',
{
targets: {
node: 'current',
},
},
],
],
};
typescript 설정
$ yarn add --dev @babel/preset-typescript
// babel.config.js
module.exports = {
presets: [
['@babel/preset-env', {targets: {node: 'current'}}],
+ '@babel/preset-typescript',
],
};
스크립트 설정
// package.json
{
//...
"scripts": {
"test": "jest"
},
// ...
}
기본 문법
Grouping
const myBeverage = {
delicious: true,
sour: false,
};
// Unit Test 모음
describe('my beverage', () => {
// Unit Test
test('is delicious', () => {
expect(myBeverage.delicious).toBeTruthy();
});
// Unit Test (it은 test의 alias입니다.)
it('is not sour', () => {
expect(myBeverage.sour).toBeFalsy();
});
});
전/후 처리
const fn = () => {};
const timeout = 5000; // default
// timeout 은 how long to wait before aborting라는데 동작이.. 잘 모르겠음
// 모든 테스트의 전/후에 실행
beforeAll(fn, timeout)
afterAll(fn, timeout)
// 해당 테스트 전/후에 실행
beforeEach(fn, timeout)
afterEach(fn, timeout)
Matchers
/*
주요 matchers
*/
const expectedResult = null; // 예상 결과값
toBe(expectedResult); // primitive 값만 비교
toEqual(expectedResult); // object 값 비교
toBeTruthy(); // true 인지
toBeFalsy(); // false 인지
toThrow(); // 예외 발생 여부 확인
const arrayItem = null;
toContain(arrayItem); // === (strict check)했을 때 item이 array에 속할 때
const arrayItemObject = {key: 'value'};
toContainEqual(arrayItemObject); // array에 object item이 속할 때 (key-value 쌍 포함)
not.toXxx(); // 부정
const testTarget = null;
expect(testTarget).toThrow();
// 위 경우 testTarget은 함수여야합니다.
테스트 코드 선택 실행
// only : 해당 테스트만 실행
describe.only('', () => { /* ... */});
test.only('', () => { /* ... */});
it.only('', () => { /* ... */});
// skip : 해당 테스트만 제외하고 실행
describe.skip('', () => { /* ... */});
test.skip('', () => { /* ... */});
it.skip('', () => { /* ... */});
비동기 처리
setTimeout
function fetchUser(id, cb) {
setTimeout(() => {
const user = {/* ... */}
cb(user);
}, 200);
}
test('fetch a user', (done) => {
fetchUser(1, (user) => {
expect(user).toEqual(/* ... */);
done(); // 비동기 코스트 테스트라는 걸 명시적으로 표시
});
});
Promise
function fetchUser(id) {
return new Promise((resolve) => {
setTimeout(() => {
const user = {/* ... */}
resolve(user);
}, 200);
})
}
test('fetch a user', (done) => {
// 1. Promise를 리턴
return fetchUser(1).then((user) => {
expect(user).toEqual(/* ... */);
});
// 2. 간단한 방법
return expect(fetchUser(1)).resolves.toEqual(/* ... */);
});
async/await
function fetchUser(id) {
return new Promise((resolve) => {
setTimeout(() => {
const user = {/* ... */}
resolve(user);
}, 200);
})
}
test('fetch a user', (done) => {
// 1. 기본 방법
const user = await fetchUser(1);
expect(user).toEqual(/* ... */);
// 2. 간단한 방법
await expect(fetchUser(1)).resolves.toEqual(/* ... */);
});
Mocking
단위 테스트 코드가 의존하는 데이터를 가짜(mock)로 대체하는 기법
- mock object 생성 가능
- 테스트를 실행하는 동안 mock object에 발생한 일들을 기억
Mock Functions
jest.fn()
가짜 함수 생성
import { register, deregister } from './userService';
import * as messageService from './messageService';
// 실제 sendEmail, sendSMS의 동작은 중요하지 않다.
// register, deregister에서 messageService의 sendEmail, sendSMS가 어떻게 불리는지를 테스트해야한다.
messageService.sendEmail = jest.fn();
messageService.sendSMS = jest.fn();
const sendEmail = messageService.sendEmail;
const sendSMS = messageService.sendSMS;
beforeEach(() => {
sendEmail.mockClear();
sendSMS.mockClear();
});
const user = {
email: 'test@email.com',
phone: '012-345-6789',
};
test('register sends messeges', () => {
register(user);
expect(sendEmail).toBeCalledTimes(1);
expect(sendEmail).toBeCalledWith(user.email, '회원 가입을 환영합니다!');
expect(sendSMS).toBeCalledTimes(1);
expect(sendSMS).toBeCalledWith(user.phone, '회원 가입을 환영합니다!');
});
위 코드는 모듈의 함수들을 일일이 jest.fn()으로 mocking한다.
아래 코드는 jest.mock()을 사용하여 모듈을 전체 mocking하여 필요한 함수만 사용한다.
import { sendEmail, sendSMS } from "./messageService"
jest.mock('../src/messageService');
const { sendEmail, sendSMS } = require('../src/messageService');
beforeEach(() => {
sendEmail.mockClear()
sendSMS.mockClear()
})
jest.spyOn(object, methodName)
가짜 함수로 대체하지 않고, 특정 함수가 어떻게 호출되었는지를 확인
const calculator = {
add: (a, b) => a + b,
}
const spyFn = jest.spyOn(calculator, "add")
const result = calculator.add(2, 3)
expect(spyFn).toBeCalledTimes(1)
expect(spyFn).toBeCalledWith(2, 3)
expect(result).toBe(5)
참고
'개발이야기 > 기타' 카테고리의 다른 글
[TDD] TDD의 3단계 과정으로 간단한 모듈 구현 (0) | 2021.07.17 |
---|---|
[Git] fork 없이 fork 한 것처럼 사용하기 (0) | 2021.07.01 |
reply