💻 Frontend
테스트 코드 문법 공부하기
category
💻 Frontend
테스트 코드 문법JestmatcherbeforeEach, beforeAll, afterEach, afterAllRTLrendergetBy, findBy, queryByfireEvent, userEvent마무리References
테스트 코드 문법
최근에 과제를 하면서 단위 테스트를 작성해보는 시간을 가졌다. 처음부터 끝까지 테스트 코드를 작성하는 건 처음이라 이번 포스팅을 통해 테스트 코드 문법에 대해 정리해보려고 한다!
테스트 코드의 개념에 대해서는 이전 블로그를 참고해주세요.
Jest
Jest는 기본적으로
*.test.*
의 네이밍을 가진 파일을 테스트 파일로 인식하며, 해당 파일 안에 있는 코드를 실행합니다.일반적으로 소프트웨어를 테스트 하는 과정을 생각하면
- 특정한 동작을 수행한다.
- 동작을 수행한 결과가 기대한 상황과 일치하는지 판단한다.
다음과 같은 과정을 거치는데요, 테스트 코드도 이와 같은 과정으로 작성합니다.
Jest에서 2번 과정처럼 기대한 상황과 일치하는지 판단하는 함수들을 matchers라고 표현합니다.
- 특정한 동작을 수행한다.
- matcher 함수를 통해 실제 결과와 기대값이 맞는지 검증한다.
이때 1번 과정처럼 특정 동작을 수행하기 위해서는
test()
또는 it()
함수를 활용합니다.기본적으로 테스트 코드는 아래와 같은 구조를 가집니다.
test("2+2는 4다.", () => { expect(2 + 2).toBe(4); });
test
나 it
함수의 첫번째 인자는 테스트 케이스에 대해 설명하는 것입니다. 두번째 인자는 콜백함수로 어떠한 것을 테스트하는지 로직을 담은 코드입니다.expect
는 테스트에서 어떠한 것을 검증할 것인지 검사하는 역할입니다. 하나의 콜백 안에 여러 expect를 수행할 수 있으며, 하나라도 기대 값과 일치하지 않을 경우 해당 테스트는 실패한 것으로 간주됩니다.matcher
Jest에서 주로 사용되는 matcher는 아래와 같습니다.
toBe
: expect의 인자가 toBe의 인자와 일치하는지 검사합니다.
toEqual
: 객체가 동일한지 판단 해줍니다.
toBeNull
,toBeUndefined
toContain
: 특정한 요소를 포함하고 있는지 검사합니다.
not
: matcher의 기대값을 반대로 변경해줍니다.- ex. not.toBe(4) → 4가 아니길 기대한다.
beforeEach, beforeAll, afterEach, afterAll
Jest에서 테스트 전/후 처리하는 문법을 통해 여러 테스트 케이스의 중복 코드를 줄일 수 있습니다.
- before 키워드: 테스트 전 작업 수행
- after 키워드: 테스트 후 작업 수행
- beforeEach, afterEach
- 테스트 마다 반복 실행이 필요한 작업
- 동일 레벨 또는 하위 레벨의 테스트가 실행 될 때 마다 반복적으로 실행
- beforeAll, afterAll
- 딱 한번 실행이 필요한 작업
- 동일 레벨 또는 하위 레벨의 테스트가 실행 될 때 딱 한번만 실행
beforeAll(() => { return initializeCityDatabase(); }); afterAll(() => { clearCityDatabase(); });
더 자세한 내용은 아래 블로그 참고 바랍니다.
RTL
Jest 만으로도 코드를 테스트할 수 있지만, React는 UI 라이브러리이기 때문에 순수 Jest 만드로 테스트 하기엔 어려움이 있습니다. 따라서 UI를 렌더링하는 부분과 동작하는 부분은 라이브러리를 통해 테스트를 수행해야 합니다.
많이 사용되는 라이브러리로는 Enzyme와 React-Testing-Library(RTL)이 있습니다.
Enzyme는 “구현"을 테스트하는 것에 초점이 맞춰져 있는 라이브러리이며, RTL은 “결과"를 테스트하는 것에 초점이 맞춰진 라이브러리입니다. 이 중 어떤 것을 사용해야한다는 정답은 존재하지 않으며, 테스트하고자 하는 목적에 따라서 두개를 적절하게 선택해서 사용하기도 합니다.
RTL에서 테스트 과정은 다음과 같습니다.
- 테스트할 화면을 렌더링 한다.
- 특정한 동작을 수행한다. (클릭 같은 이벤트 등)
- 동작을 수행한 결과가 기대한 상황과 일치하는지 판단한다.
render
1번 과정에서 테스트 할 화면을 렌더링하기 위해 render라는 주요 api가 있습니다.
예를 들어 <header /> 를 렌더링 하고 싶으면 아래와 같이 작성하면 됩니다.
import {render} from '@testing-library/react' render(<header />)
getBy, findBy, queryBy
2번 과정에서 특정한 동작을 수행하기 위해 요소를 가져오는 메서드들이 있습니다.
JS에서 DOM에 접근할 때
getElemtBy~~~
, querySelector
과 같이 렌더링 된 요소에게 접근할 수 있는 것과 같이 RTL에서는 크게 3가지 종류로 구분됩니다.getBy~~~
: 해당 요소가 현재 DOM상에 있는지 동기적으로 확인합니다. 만약 찾는 요소가 없을 경우 예외를 던집니다.
findBy~~~
: 해당 요소가 현재 DOM상에 있는지 비동기적으로 확인합니다. 해당 요소를 찾기 위해 일정 시간을 기다리며, 시간이 지난 후에도 찾을 수 없는 경우 예외를 던집니다.
queryBy~~~
: getBy와 동일하게 동작하지만 찾는 요소가 없을 경우 예외를 던지는 것이 아닌 null을 반환합니다.
예시
getByRole
getByText
getByLabelText
getByPlaceholderText
getByDisplayValue
getByAltText
getByTitle
getByTestId
const buttonElement = screen.getByText(/퀴즈 풀러 가기/i);
더 자세한 설명은 해당 공식문서를 참고 바랍니다.
fireEvent, userEvent
또한 2번 과정에서 특정한 동작을 수행하기 위해 이벤트를 발생시키는 메소드가 있습니다.
fireEvent는 React 컴포넌트를 테스트할 때 사용자 동작을 모의하고 특정 이벤트를 발생시켜 컴포넌트의 상태 변화 및 렌더링을 확인하는 데 유용합니다.
userEvnet는 fireEvent와 마찬가지로 유저 상호작용을 컴포넌트에서 테스트하기 위해 제공되는 함수로 거의 똑같이 동작하지만, 실제 사용자가 수행하는 것과 보다 유사하게 이벤트를 발생시킨다고 합니다.
userEvent.click(buttonElement);
더 자세한 설명은 해당 블로그를 참고 바랍니다.
마무리
테스트 코드를 작성해야된다고 했을 때 막연하기만 했는데 막상 직접 작성해보니 감이 조금 잡혔다. 코드를 작성하면서 여러 matcher와 RTL에 대해 자세히 알게되었다.
여러 요소를 담으려고 하다보니 블로그 글로 대체한 부분이 많았다. 다음 포스팅은 모킹에 대해 작성해보고싶다.