Notice
Recent Posts
Recent Comments
관리 메뉴

즐겁게, 코드

프론트엔드 테스팅 네 번째 걸음 : 리액트 테스팅 본문

🎨 프론트엔드/Testing

프론트엔드 테스팅 네 번째 걸음 : 리액트 테스팅

Chamming2 2021. 8. 23. 01:20

지금까지는 jest 와 함께 평범한 자바스크립트 코드를 테스트해봤는데요, 이번에는 리액트 어플리케이션의 레이아웃 테스팅을 진행해보도록 하겠습니다.

테스팅 라이브러리

리액트 어플리케이션을 테스트할때는 주로 에어비앤비에서 만든 enzyme이나 react-testing-library(앞으로는 간단히 RTL이라 부르겠습니다.) 를 사용합니다.

 

둘의 차이로는 enzymeImplementation Driven Test(구현 주도 테스트) 라는 목표에 따라 어플리케이션이 내부적으로 어떻게 동작하는지에 초점을 맞췄다면, RTLBehavior 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의 핵심은 '사용자의 동작을 시뮬레이션했을 때, 화면에 보이는 뷰가 의도된 대로 나타나느냐?' 를 테스트하는 것 같은데, 이전 글에서 예로 들었던 단순한 함수를 테스트하는 것보다 훨씬 까다로운 것 같습니다. 😆

 

다음 글에서는 리액트의 비동기 렌더링 결과물을 테스트하는 방법을 소개하고, 테스팅 시리즈를 마무리하도록 하겠습니다.

반응형
Comments
소소한 팁 : 광고를 눌러주시면, 제가 뮤지컬을 마음껏 보러다닐 수 있어요!
와!! 바로 눌러야겠네요! 😆