일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
- k8s
- 알고리즘
- 프론트엔드
- 쿠버네티스
- 솔리디티
- AWS
- react
- JavaScript
- VUE
- 컴퓨터공학
- 리액트
- 블록체인
- BFS
- 자바스크립트
- es6
- HTML
- 클라우드
- 이더리움
- 백준
- TypeScript
- 백엔드
- 이슈
- kubernetes
- docker
- 웹
- next.js
- 파이썬
- CSS
- 가상화
- 타입스크립트
- Today
- Total
즐겁게, 코드
프론트엔드 테스팅 네 번째 걸음 : 리액트 테스팅 본문
지금까지는 jest
와 함께 평범한 자바스크립트 코드를 테스트해봤는데요, 이번에는 리액트 어플리케이션의 레이아웃 테스팅을 진행해보도록 하겠습니다.
테스팅 라이브러리
리액트 어플리케이션을 테스트할때는 주로 에어비앤비에서 만든 enzyme
이나 react-testing-library
(앞으로는 간단히 RTL
이라 부르겠습니다.) 를 사용합니다.
둘의 차이로는 enzyme
이 Implementation Driven Test(구현 주도 테스트) 라는 목표에 따라 어플리케이션이 내부적으로 어떻게 동작하는지에 초점을 맞췄다면, RTL
은 Behavior Driven Test(행위 주도 테스트) 라는 목표 아래에서 실제 사용자의 행위와 사용자가 보는 화면을 테스트하는데 초점을 맞췄습니다.
두 테스팅 라이브러리 모두 장점이 있기에 뭐가 더 낫다고는 할 수 없지만, 이번 글에서는 react-testing-library
를 사용하는 방법을 소개하도록 하겠습니다.
테스팅 설정
테스팅을 위해 먼저 리액트 프로젝트를 세팅한 후, @testing-library/react
와 @testing-library/jest-dom
을 데브디펜던시로 설치합니다.
CRA로 프로젝트를 세팅했다면
jest
가 이미 설치되었기 때문에jest
를 추가로 설치하면 오류의 원인이 됩니다.
// yarn
yarn add -D testing-library/react testing-library/jest-dom
// npm
npm i -D testing-library/react testing-library/jest-dom
이제 테스팅 준비는 모두 끝났습니다.
테스트 파일 작성
먼저 테스트를 위해 버튼을 눌러 숫자를 조작하는 간단한 카운터를 만들어 보겠습니다.
// Counter/Counter.js
import React, { useState } from "react";
const Counter = () => {
const [count, setCount] = useState(0);
const increase = () => {
setCount(count + 1);
};
const decrease = () => {
setCount(count - 1);
};
return (
<>
<div className="count">{count}</div>
<button onClick={increase} className="button-increase">
+
</button>
<button onClick={decrease} className="button-decrease">
-
</button>
</>
);
};
export default Counter;
이제 동일한 디렉토리에 테스트 코드를 작성하고, 화면의 버튼을 누르면 숫자가 올라가는지를 테스트해보겠습니다.
// Counter/Counter.test.js
test("Increase Count when Press + Button", () => {
});
render, screen
RTL
을 사용할 때는 먼저 DOM을 가상으로 렌더링하는 작업이 필요합니다.
이를 위해 RTL
에서는 render()
라는 함수를 지원하는데요, render()
는 인자로 주어진 컴포넌트를 테스트 범위 내에 가상으로 렌더링(마운트)하는 역할을 합니다.
테스트 코드에서 Counter
컴포넌트를 렌더링하도록 해보겠습니다.
// Counter/Counter.test.js
import { render } from "@testing-library/react";
import Counter from "./Counter";
test("Increase Count when Press + Button", () => {
render(<Counter />);
});
이제 해당 테스트는 Counter
컴포넌트를 테스트하기 시작합니다.
Tip. 테스트를 수행하기 전에는 항상 렌더링된 DOM을 원상태로 초기화(언마운트)해야 합니다.
따라서 과거에 작성된 튜토리얼들에서는@testing-library/react/cleanup-after-each
라는 모듈을 사용했을텐데요, 2021년 기준으로는 RTL에 자동으로 적용되어 있기 때문에 따로 설정하지 않으셔도 됩니다. (이슈 링크)
Counter
컴포넌트가 테스트 대상으로 설정되었으니, 이제 screen
객체와 쿼리를 통해 컴포넌트의 요소들을 선택할 수 있습니다.
쿼리를 통해 요소 선택하기
요소를 선택하는 방법은 쿼리의 조합으로 이루어집니다.
쿼리는 요소를 불러올 방법
+ 불러올 조건
으로 구성되는데요, 요소를 불러오는 방법은 크게 get
, query
, find
가 있습니다.
get
: 조건에 일치하는 DOM 요소를 선택합니다. 만약 선택할 요소가 존재하지 않을 경우에는 테스트에 실패합니다.query
: 조건에 일치하는 DOM 요소를 선택합니다. 존재하지 않으면null
을 리턴하지만, 테스트를 실패하지는 않습니다.find
: 쿼리는 조건에 일치하는 DOM 엘리먼트 하나가 나타날 때까지 기다렸다가, 해당 DOM을 선택하는 프라미스를 반환합니다. 기본 타임아웃 시간(1초)를 경과해도 나타나지 않으면 테스트에 실패합니다.
쿼리 예시
screen.getByText("Hello")
: "Hello" 라는textContent
를 갖는 첫 번째 요소를 찾습니다.screen.queryAllByAlt("thumbnail")
: "thumbnail" 이라는alt
속성값을 갖는 모든 요소들을 찾아 배열로 리턴합니다. 만약 찾지 못해도 테스트를 실패하지는 않습니다.
가능한 조건의 목록은 공식 문서 에서 확인할 수 있습니다.
그러나 생각보다 적은 조건만 지원되는 것을 알 수 있는데, id
, class
등의 선택자를 활용하기 위해서는 document
객체를 사용해 document.querySelector()
등의 메서드를 사용해야 합니다.
여기까지 배운 내용을 토대로 카운터를 증가시키는 +
버튼을 선택해 보겠습니다.
// Counter/Counter.test.js
import { render, screen } from "@testing-library/react";
import Counter from "./Counter";
test("Increase Count when Press + Button", () => {
render(<Counter />);
// "+" 버튼을 선택합니다.
const increaseButton = screen.getByText("+");
});
이벤트 시뮬레이션
이제 버튼을 눌렀을 때 숫자가 증가하는지를 테스트해야 합니다.
물론 저희가 직접 버튼을 누를 필요는 없고, @testing-library/user-event
라는 라이브러리를 통해 DOM 이벤트를 시뮬레이션할 수 있습니다.
// yarn
yarn add -D @testing-library/user-event
// npm
npm add -D @testing-library/user-event
// Counter/Counter.test.js
import { render, screen } from "@testing-library/react";
import Counter from "./Counter";
// @testing-library/user-event 로부터 default import 로 불러옵니다.
import userEvent from '@testing-library/user-event';
test("Increase Count when Press + Button", () => {
render(<Counter />);
const increaseButton = screen.getByText("+");
// "+" 버튼을 클릭하도록 시뮬레이션합니다.
userEvent.click(increaseButton);
});
userEvent
객체를 통해 클릭 이벤트를 시뮬레이션한 모습입니다.
이제, 마지막으로 버튼을 누른 결과가 기대값과 일치하는지 테스트하면 됩니다.
// Counter/Counter.test.js
import { render, screen } from "@testing-library/react";
import Counter from "./Counter";
import userEvent from '@testing-library/user-event';
test("Increase Count when Press + Button", () => {
render(<Counter />);
const increaseButton = screen.getByText("+");
userEvent.click(increaseButton);
// 쿼리를 통해서는 아이디나 클래스명을 선택할 수 없어 document 객체를 사용해야 합니다.
// 테스트가 제대로 작동하는지 확인하기 위해, 일부러 기대값을 "1" 이 아닌 "2" 로 전달하겠습니다.
expect(document.querySelector(".count").textContent).toBe("2");
});
// CRA로 프로젝트를 구성했다면 아래 명령어를 통해 테스트를 실행할 수 있습니다.
// 테스트는 기본적으로 --watch 옵션이 적용되어 있습니다.
// yarn
yarn test
// npm
npm test
테스트가 동작한 모습입니다!
RTL
의 핵심은 '사용자의 동작을 시뮬레이션했을 때, 화면에 보이는 뷰가 의도된 대로 나타나느냐?' 를 테스트하는 것 같은데, 이전 글에서 예로 들었던 단순한 함수를 테스트하는 것보다 훨씬 까다로운 것 같습니다. 😆
다음 글에서는 리액트의 비동기 렌더링 결과물을 테스트하는 방법을 소개하고, 테스팅 시리즈를 마무리하도록 하겠습니다.
'🎨 프론트엔드 > Testing' 카테고리의 다른 글
프론트엔드 테스팅 세 번째 걸음 : 목업 테스팅 (0) | 2021.08.20 |
---|---|
프론트엔드 테스팅 두 번째 걸음 : 비동기 코드 테스팅 (0) | 2021.08.19 |
프론트엔드 테스팅 첫걸음 : jest 사용하기 (0) | 2021.08.19 |