React Testing Library

카테고리 React

📌 React Testing Library

리액트에서 TDD 방식의 개발을 하기 위해서 테스팅 라이브러리를 사용해보자.

🎯 목적

1. 버그 캐치

테스트를 통해 예상치 못한 여러가지 버그들을 사전에 확인하기 용이하다.

2. 애플리케이션 신뢰도 향상

어떠한 근거로 이 애플리케이션이 제대로 동작하는지 근거를 뒷받침하고 이 근거에 대한 신뢰도를 높일 수 있다.

3. 질문 및 답변 시간 축소

누군가 어떤 컴포넌트가 어떻게 동작하는지 질문했을 때 그에대한 답변으로 이 테스트를 보여주기만 하면된다. 그럼으로 시간을 절약하고 유지보수성을 높일 수 있다.

4. 문서 역할

테스트라는 문서를 제공함으로서 2,3번의 장점을 가능하도록 한다.

💼 사용방법

우선 해당 라이브러리를 설치해준다.

1
2
3
4
5
npm install --save-dev @testing-library/react

npm install --save-dev @testing-library/dom

npm install --save-dev @testing-library/user-event

리액트 테스팅 라이브리러DOM 테스트 라이브러리, 사용자 행동 테스트 라이브러리를 설치하여 테스트 개발을 해보자.

🦖 Component 테스트

✏️ 테스트 코드 작성하는 방법

  1. 테스트가 필요한 컴포넌트 렌더링

  2. 컴포넌트의 요소 탐색

  3. 요소와의 상호작용

  4. 어설션 테스트 결과와 기대 값이 일치하는 지 확인

우선 컴포넌트를 생성해주자.

📌 Tip 컴포넌트 생성

컴포넌트를 쉽게 생성하기 위해 yamoo9님이 제공해준 Tool을 사용해보도록하자.

1
npx degit yamoo9/create-react-component create-react-component
1
2
3
4
5
6
7
8
// package.json
{
...
"scripts" :
...,
"rc": "node create-react-component create",
"rd": "node create-react-component delete"
}
1
2
npm run rc -- 컴포넌트_이름 // 컴포넌트 생성
npm run rd -- 컴포넌트_이름 // 컴포넌트 제거

ESLint 에서 테스팅 라이브러리를 사용하게되면 오류를 띄워주는데 이에 대한 Lint 경고를 꺼두자.

1
2
3
4
5
// package.json
{
"eslintConfig":{...},
"testing-library/no-debugging-utils": "off"
}

예제

1. 컴포넌트가 렌더링 확인

1
2
3
4
// ToggleButton.jsx
export function ToggleButton({ onText, offText, on }) {
return <div>{on ? onText : offText}</div>;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// ToggleButton.test.jsx
import { render, screen } from "@testing-library/react";
import { ToggleButton } from "./ToggleButton";

describe.only("ToggleButton Test Start!", () => {
test("컴포넌트가 정상적으로 렌더링 되었습니다.", () => {
render(<ToggleButton onText="1" offText="0" />);

const offTextElement = screen.getByText("0");
const onTextElement = screen.queryByText("1");

expect(offTextElement).toBeInTheDocument();
expect(onTextElement).not.toBeInTheDocument();
});
});
  • getByText() : 가상으로 그려진 문서에 존재하는 것만 가져올 수 있다. 만약 존재 하지 않는 다면 오류를 발생시킨다.
  • queryByText() : 존재하지 않으면 오류를 발생시키지 않고 null 값으로 가져온다.

2. 활성화 상태 여부에 따라 텍스트 표시

1
2
3
4
// ToggleButton.jsx
export function ToggleButton({ onText, offText, on }) {
return <button type="button">{on ? onText : offText}</button>;
}
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
// ToggleButton.test.jsx
describe("ToggleButton 컴포넌트", () => {
test("활성 상태 여부에 따라 활성(ON)/비활성(OFF) 텍스트가 표시됩니다.", () => {
let onText = "ON";
let offText = "OFF";

render(<ToggleButton onText={onText} offText={offText} />);

let elements = screen.queryAllByRole("button");
let firstElement = elements[0];
expect(firstElement).toHaveTextContent(offText);

screen.debug();

cleanup();

render(<ToggleButton onText={onText} offText={offText} on />);

elements = screen.queryAllByRole("button");
firstElement = elements[0];

screen.debug();

expect(firstElement).toHaveTextContent(onText);
});
});
  • 만약 on 일때의 text와 off일 때의 text를 찾고 싶다면 queryAllText() 를 사용해준다,
  • cleanup()을 해줘야지만 firstElement를 확인할 때, 앞에 그려진 것을 지우고 새로 그려진 것을 비교해줄 수 있다.

3. onToggle 속성(prop)에 연결된 함수 실행 확인 & 활성 상태 컴포넌트는 ToggleButton--on 클래스 이름 포함 확인

1
2
3
4
5
6
7
8
9
10
11
12
// ToggleButton.js
export function ToggleButton({ onText, offText, on, onToggle }) {
return (
<button
type="button"
className={`ToggleButton ${on ? "ToggleButton--on" : ""}`.trim()}
onClick={onToggle}
>
{on ? onText : offText}
</button>
);
}
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
// ToggleButton.test.js
describe("ToggleButton 컴포넌트", () => {
test("`onToggle` 속성(prop)에 연결된 함수가 정상적으로 실행됩니다.", () => {
let expected = "triggering toggle event";
let received = "";

render(
<ToggleButton
onToggle={() => {
received = expected;
}}
/>
);

expect(received).not.toBe(expected);

const element = screen.queryByRole("button");

fireEvent.click(element); // click button element

expect(received).toBe(expected);
});

test(`활성 상태의 컴포넌트는 'ToggleButton--on' 클래스 이름을 포함한다.`, () => {
let expected = "ToggleButton--on";
render(<ToggleButton on />);

const element = screen.getByRole("button");
expect(element).toHaveClass(expected);
});
});
  • fireEvent() : 이벤트를 발생시켜주는 메서드이다.

  • queryByRole() : 해당 요소의 역할을 확인하는데 사용된다.

    ex) button 태그에 type을 “button”으로 명시적으로 작성하였는지…

🏓 소감

오늘 수업에서 상태를 가지지 않는 컴포넌트의 다양한 테스트 방법에 대해 실습을 진행하였다. 리액트를 TDD 방식으로 개발을 진행하게 된다면 앞서 말한 애플리케이션의 신뢰도 향상할 수 있고 테스트 문서를 생성하여 유지보수를 용이하게 할 수 있다는 생각이 들었다.

아직 jest에 익숙하지 않아 낯설고 어렵지만, 우테코에서도 jest를 사용하고 있고 앞으로 자주 사용해보면서 테스트 주도 개발에 대해 몸을 익히도록 해야겠다.

댓글 공유

  • page 1 of 1

loco9939

author.bio


author.job