Jest
테스팅 라이브러리
🔦 기본 세팅
npm i -D jest
"scripts": {"test": "jest"},
🍮 테스트 함수 작성하기
function sum(a, b) {return a + b;}module.exports = sum;
const sum = require('./calculate');test('더하기 함수 테스트', () => {expect(sum(1, 2)).toBe(3);});
🍦 toBe, toEqual
toBe로 비교
const makeObj = (id, name) => {return { id, name };};test('toBe의 문제점', () => {expect(makeObj('jke', '장경은')).toBe({ id: 'jke', name: '장경은' });});
If it should pass with deep equality, replace "toBe" with "toStrictEqual"Expected: {"id": "jke", "name": "장경은"}Received: serializes to the same string4 |5 | test('toBe의 문제점', () => {> 6 | expect(makeObj('jke', '장경은')).toBe({ id: 'jke', name: '장경은' });| ^7 | });8 |at Object.toBe (toEqual.test.js:6:33)
toEqual로 비교
const makeObj = (id, name) => {return { id, name };};test('toEqual로 변경', () => {expect(makeObj('jke', '장경은')).toEqual({ id: 'jke', name: '장경은' });});
PASS ./toEqual.test.js✓ toEqual로 변경 (1 ms)Test Suites: 1 passed, 1 totalTests: 1 passed, 1 totalSnapshots: 0 totalTime: 0.152 s, estimated 1 s
const makeObj = (id, name) => {return { id, name, age: undefined };};test('toEqual의 한계', () => {expect(makeObj('jke', '장경은')).toEqual({ id: 'jke', name: '장경은' });});
PASS ./toEqual.test.js✓ toEqual의 한계 (1 ms)Test Suites: 1 passed, 1 totalTests: 1 passed, 1 totalSnapshots: 0 totalTime: 0.15 s, estimated 1 s
toStrictEqual
const makeObj = (id, name) => {return { id, name, age: undefined };};test('toStrictEqual로 변경', () => {expect(makeObj('jke', '장경은')).toStrictEqual({ id: 'jke', name: '장경은' });});
Test Suites: 1 failed, 1 totalTests: 1 failed, 1 totalSnapshots: 0 totalTime: 0.17 s, estimated 1 s
🍬 TS에서 사용
npm i -D ts-jest @types/jest
npx ts-jest config:init
→ jest.config.js 파일이 생성됨
테스트 케이스 생성
const sum = (a: number, b: number): number => {return a + b;};test('타입스크립트 함수 테스트', () => {expect(sum(1, 2)).toBe(3);});
PASS ./ts.test.ts✓ 타입스크립트 함수 테스트Test Suites: 1 passed, 1 totalTests: 1 passed, 1 totalSnapshots: 0 totalTime: 0.877 s
🍿 Matchers
interface User {email: string;name: string;age: number;}const users: User[] = [{ email: 'j5@naver.com', name: '장경은', age: 27 },{ email: 'sh@naver.com', name: '박성희', age: 27 },{ email: 'sa@naver.com', name: '신상아', age: 31 },{ email: 'tetz@naver.com', name: '이효석', age: 39 },];const getUser = (): User[] => {return users;};const getUserNumByAge = (age: number): number => {const filteredUser: User[] = users.filter(el => el.age >= age);return filteredUser.length;};const getEmailByName = (name: string) => {const filteredUser: User[] = users.filter(el => {if (el.name === name) return el.email;});const result: string = filteredUser.length > 0 ? filteredUser[0].email : '존재하지 않는 이름입니다.';return result;};
toHaveLength
test('전체 회원의 수는 4명인가?', () => {expect(getUser()).toHaveLength(4);});
toContainEqual
test('전체 회원 중에 아래의 회원 정보를 가진 회원이 존재 하는가?', () => {expect(getUser()).toContainEqual({email: 'tetz@naver.com',name: '이효석',age: 39,});});
숫자 비교 Matchers
test('25살 이상인 회원은 3명이 넘는가?', () => {expect(getUserNumByAge(25)).toBeGreaterThan(3);});// 4명이므로 pass// 그러나 4를 입력하면 failed
toMatch
test('특정 이름을 가진 회원의 email 주소 형태가 올바른가?', () => {expect(getEmailByName('장경은')).toMatch(/^[a-zA-Z0-9+-_.]+@[a-zA-Z0-9-]+.[a-zA-Z0-9-.]+$/);});
toThrow
const throwErr = (): never => {throw new Error('에러 발생!');};test('에러가 발생하는지 테스트', () => {expect(() => throwErr()).toThrow();});
not
test('null', () => {const n = null;expect(n).toBeNull();expect(n).not.toBeUndefined();});
🍥 비동기 테스트
Callback
const getNameCB = (callback: (str: string) => void): void => {const name: string = 'jke';setTimeout(() => {callback(name);}, 2000);};
test('2초 뒤에 이름을 받아오는 콜백 함수 테스트', done => {function callbackFunc(name: string): void {expect(name).toBe('jke');done();}getNameCB(callbackFunc);});
const getNameCB = (callback: (str: string) => void): void => {const name: string = 'jke';setTimeout(() => {try {if (Math.floor(Math.random() * 2) % 2 === 0) {console.log('정상 케이스');callback(name);} else {console.log('에러');}} catch (err) {callback(err);}}, 2000);};test('비동기 콜백 함수 테스트', done => {function callbackFunc(data: any): void {try {if (data instanceof Error) {expect(data.message).toBe('에러');} else {expect(data).toBe('jke');}done();} catch (err) {done(err);}}getNameCB(callbackFunc);});
Promise
const getNamePromise = (): Promise<string> => {const name = 'jke';return new Promise<string>((resolve, reject) => {setTimeout(() => {resolve(name);}, 2000);});};test('2초 후에 이름을 받아오는 프로미스 함수 테스트', () => {return getNamePromise().then((name: string) => {expect(name).toBe('jke');});});
const getNamePromise = (): Promise<string> => {const name = 'jke';return new Promise<string>((resolve, reject) => {setTimeout(() => {if (Math.floor(Math.random() * 2) % 2 === 0) {resolve(name);} else {reject(new Error('에러'));}}, 2000);});};test('2초 후에 이름을 받아오는 프로미스 함수 테스트', () => {return getNamePromise().then((name: string) => {expect(name).toBe('jke');}).catch(err => {expect(err.message).toBe('에러');});});
Async / Await
const getNameAsync = (id: string): Promise<string> => {return new Promise<string>((res, rej) => {setTimeout(() => {if (id === 'jke') {console.log('정상 케이스');res('장경은');} else {console.log('에러 케이스');rej(new Error('id가 다릅니다.'));}}, 2000);});};test('2초 후에 이름을 async/await로 받아오는 함수 테스트', async () => {try {const result: any = await getNameAsync('jke');expect(result).toBe('장경은');} catch (err) {expect(err.message).toBe('id가 다릅니다.');}});