-
728x90
1. Jest란?
자바스크립트를 위한 테스트 프레임워크로 facebook에서 오픈스스화한 프로젝트이며 사용성이 좋고, 가장 인기 있는 프로젝트.
일반적으로 자바스크립트 코드에 대한 단위테스트, 통합테스트, 비동기 코드 테스트, mocking을 지원한다.
2. Assertion Matchers
Assertion Matchers는 테스트 케이스를 작성할 때 사용되는 함수로 풍부한 matcher를 제공하여 여러상황에서 match를 체크한다.
expect()는 expectation object를 리턴하고 이 object의 메서드를 이용해서 여러 매칭 상황을 assert한다.
// a, b, c를 받아서 a와 b의 제곱의 합이 c의 제곱과 같은지 검사하여 true, false를 리턴 function isPythagorean(a, b, c) { return a * a + b * b === c * c; } // id, title, content를 객체로 리턴 function createTodo(id, title, content) { return { id, title, content }; } // user객체에서 name, age, address를 추출해서 배열로 리턴 function transformUser(user) { const { name, age, address } = user; return [name, age, address]; }
- .toBe(value): 기대하는 값과 일치하는지 확인
test('Should 3, 4, 5 pythagorean', () => { expect(isPythagorean(3, 4, 5)).toBe(true); }); test('Should 3, 4, 6 not pythagorean', () => { expect(isPythagorean(3, 4, 6)).toBe(false); });
첫 번째 테스트 케이스는 isPythagorean함수에 3, 4, 5를 넘긴 후 .toBe를 이용해서 피타고라스 정리를 만족하는지 확인, 두번째 케이스는 3, 4, 6을 넘긴 후 만족하지 않는지를 확인한다.
- .toEqual(value): 객체 또는 배열의 값을 재귀적으로 확인
- .toMatch(regexp | string): 문자열이 정규표현식과 일치하는지 확인
test('Should create user', () => { const id = 1, title = "Test todo", content = "Test content"; expect(createTodo(id, title, content)).toEqual({ id, title, content }); }); test('Should create user', () => { const id = 1, title = "Test todo", content = "Test content"; expect(createTodo(id, title, content).title).toMatch("Test todo"); });
첫 번째 테스트 케이스는 id, title, content를 생성 후 createTodo함수에 인자로 넘겨서 주어진 id, title, content와 일치하는지 확인.
두번째 케이스는 createTodo함수가 생성한 객체의 title이 "Test todo" 와 일치하는지 확인한다.
- .toContain(item): 배열이나 문자열에 특정 항목이 포함되어있는지 확인
- .not: 부정. 특정 조건이 포함되지 않는지 확인
test('Should contain name after transformUser', () => { const user = { name: "test name", age: 20, address: "test address" }; expect(transformUser(user)).toContain("test name"); }) test('Should contain name after transformUser', () => { const user = { name: "test name", age: 20, address: "test address" }; expect(transformUser(user)).not.toContain(30); })
첫 번째 테스트 케이스는 transformUser 함수가 주어진 사용자 객체로부터 "test name"을 포함하는지 확인.
두번째 테스트 케이스는 transformUser 함수가 주어진 객체로부터 30을 포함하지 않는지를 확인
3. Async assertion
비동기적으로 실행되는 코드를 테스트할 때 사용되며,
callback 패턴의 경우 test() 함수가 제공하는 done()함수를 활용해서 콜백이 끝나고 done()함수를 호출.
에러가 발생하면 done()인자로 에러를 넘긴다.
Promise패턴의 경우 async/await을 활용하거나 Promise를 리턴한다.
function isPythagoreanAsync(a, b, c) { // Promise 객체 생성 return new Promise(resolve => { // 5초(500밀리초) 후에 실행 setTimeout(() => { const result = isPythagorean(a, b, c); if (result) return resolve(result); // result가 true면 결과값 true 반환 reject(new Error("Not pythagorean")); // false면 "Not pythagorean" 에러 반환 }, 500); }); }
- .resolves / .rejects: Promise가 resolve, reject될 때 반환값 확인
test('Should 3, 4, 5 be pythagoreanasync', (done) => { isPythagoreanAsync(3, 4, 5).then(done).catch(done); }); test('Should 3, 4, 5 be pythagoreanasync', () => { return expect(isPythagoreanAsync(3, 4, 5)).resolves.toBe(true); }); test('Should 3, 4, 6 be not pythagoreanasync', () => { return expect(isPythagoreanAsync(3, 4, 6)).rejects.toBe("Not pythagorean"); });
첫번째 테스트 코드는 3, 4, 5가 피타고라스 정리를 만족하는지 비동기적으로 확인하는데, Promise가 resolve, reject되면
done 콜백 함수를 호출하게 된다.
두번째 테스트 코드는 resolves matcher를 활용해서 리턴값이 true인지 확인한다.
세번째 테스트 코드는 3, 4, 6이 피타고라스 정리를 만족하지 않는지 확인하고 rejects matcher를 활용해서 에러가 "Not pythagorean"인지 확인한다.
4. Mock functions
jest.fn()을 활용해서 mock function객체를 만들고 mockReturnValueOnce() 등으로 리턴하는 값을 임의로 조작한다.
jest.fn()은 여러번 호출하면, 순서대로 세팅된 값을 반환한다.
mockResolveValue()로 promise가 resolve하는 값을 조작한다.
jest.mock()으로 특정 모듈을 mocking한다.
- toHaveBeenCalled: 이 함수가 호출되었는지 확인
- toHaveBeenCalledWith(arg1, arg2,...): 이 함수가 특정 인자와 함께 호출되었는지 확인
- toHaveBeenLastCalledWith(arg1, arg2, ...): 마지막으로 특정 인자와 함께 호출되었는지 확인
5. Lifecycle functions
- beforeEach(callback): 테스트가 실행되기 전 호출
- afterEach(callback): 테스트가 실행된 후 호출
- beforeAll(callback): 테스트 케이스가 시작되기 전 한 번 실행
- afterAll(calback): 테스트 케이스가 완료된 후 한 번 실행
beforeEach(() => { setupMockData(); }); afterEach(() => { clearMockData(); });
6. Grouping
- describe(): 테스트 케이스를 만들 때 사용. 테스트를 그룹화해서 관련된 테스트 케이스를 하나로 묶음
- test(): 각각의 테스트 케이스를 정의할 때 사용. 특정 기능 또는 모듈의 동작을 테스트
describe('This is group 1', () => { describe('This is inner group 1', () => { test('Test 1', () => {}); }); describe('This is inner group 2', () => { test('Test 2', () => {}); }); });
7. Snapshot testing
Jest에서 스냅샷 테스팅은 UI 컴포넌트의 렌더링 결과를 저장 후 동일한 컴포넌트가 변경되었는지 확인하는데 사용된다.
- toMatchSnapshot(): 기존에 스냅샷이 없었을 경우 .snap 파일을 만들고 기존 스냅샷이 있을 경우 새로운 스냅샷과 비교해서 변경사항이 있으면 테스트는 실패한다.
- toMatchlnlineSnapshot(): 별도의 스냅샷 파일을 만들지 않고 이 경우 어떻게 스냅샷이 쓰였는지 하나의 파일안에 알 수 있게 된다.
test('Snapshot test form', () => { const { container } = render(<MyForm />); expect(container.firstChild).toMatchSnapshot(); }); test('Snapshot test form', () => { const { container } = render(<MyForm />); expect(container.firstChild).toMatchInlineSnapshot(); });
첫번째는 <MyForm />컴포넌트를 렌더링 후 container 객체로부터 가져오는데
toMatchSnapshot()함수를 사용해서 현재 렌더링된 결과를 이전에 저장된 스냅샷과 비교한다.
두번째는 toMatchInlineSnapshot() 함수를 사용해서 인라인 스냅샷과 현재 렌더린된 결과를 비교한다.
expect(container.firstChild).toMatchInlineSnapshot(` <div> <div> <img alt="Placeholder" src="https://via.placeholder.com/150"/> </div> <form id="programming-form"> <fieldset> <legend>Basic Info</legend> <label for="username">Username</label> <!-- ... --> </fieldset> </form> </div> `);
expect 함수는 container.firstChild의 내용이 인라인 스냅샷과 일치하는지 확인하고, 일치하지 않는다면 테스트는 실패하게 된다.
그래서 이미지나 form의 id를 변경했다면 변경이 의도된 것인지, 아니면 실수로 발생한것인지 확인할 수 있다.
'React, Next' 카테고리의 다른 글
[React] React Testing Library(RTL)와 Jest (0) 2023.11.07 [React] React Testing Library 활용하기 (0) 2023.11.02 [React] React 테스팅 (0) 2023.10.31 [React] useState와 useRef (1) 2023.10.30 [React] Create React App대신 Vite로 React 사용하기 (0) 2023.10.29 댓글