Jest React 컴포넌트/스냅샷 테스트

2022년 11월 29일

TOC

Jest

RTL(React Testing Library)은 React 테스팅을 위해 React App에 내장되어 있는 시스템이다. RTL에는 Jest가 포함되어 있어 이를 통해 기능 테스트를 진행할 수 있다.

Jest 컴포넌트 테스트

테스팅 라이브러리 Jest 사용하기

지난 Jest 포스팅에 이어서 Jest를 사용하여 React 프로젝트를 테스트해 보기로 했다.

먼저 create react-app으로 테스트를 진행할 React 프로젝트를 생성했다.

yarn create react-app jest-tutorial

React는 기본 테스트 도구로 Jest를 사용하기 때문에 별도 설치 없이 사용할 수 있고, src 디렉터리에 App.test.js가 존재하는 걸 확인할 수 있다. learn react 링크의 렌더링을 테스트하는 코드가 작성되어 있다.

import { render, screen } from "@testing-library/react"
import App from "./App"

test("renders learn react link", () => {
  render(<App />)
  const linkElement = screen.getByText(/learn react/i)
  expect(linkElement).toBeInTheDocument()
})

yarn test를 해보면 다음과 같이 결과가 출력된다.

$ yarn test
yarn run v1.22.18
$ react-scripts test
 PASS  src/App.test.js
  ✓ renders learn react link (16 ms)

Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        0.785 s
Ran all test suites related to changed files.

다른 컴포넌트에서 테스트를 진행하기 위해 기존 테스트 코드를 삭제하고, Container 컴포넌트와 Container.test.js를 작성했다.

jestsrc

// App.js

import Container from "./Container"

const id = null

function App() {
  return (
    <div>
      <Container id={id} />
    </div>
  )
}

export default App
// Container.js

// 삼항 연산자로 id 값이 있는 경우 id를 출력, 그렇지 않으면 h1 태그 출력
function Container({ id }) {
  return id ? <h1>{id}</h1> : <h1>id가 발견되지 않습니다.</h1>
}

export default Container
// Container.test.js

import { render, screen } from "@testing-library/react"
import Container from "./Container"

const id = 123

// id 값이 있는지 검사하는 테스트
test("id 값 테스트", () => {
  const el = <Container id={id} />
  expect(el).toMatchSnapshot()
})
# 검사 성공
 PASS  src/Container.test.js
  ✓ id 값 테스트 (1 ms)

Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   1 passed, 1 total
Time:        0.229 s, estimated 1 s
Ran all test suites.

SnapShot

여러 가지 테스트를 추가할 수 있는데, id 값 확인과 함께 id가 123이 맞는지 확인하는 테스트도 해볼 수 있다.

import { render, screen } from "@testing-library/react"
import Container from "./Container"

const id = 123

test("id 값 테스트", () => {
  const el = <Container id={id} />
  expect(el).toMatchSnapshot()
})

test("id가 123인지 검사", () => {
  render(<Container id={id} />)
  const containerElement = screen.getByText(/123/i)
  expect(containerElement).toBeInTheDocument()
})
# if id = 123
 PASS  src/Container.test.js
  ✓ id 값 테스트 (2 ms)
  ✓ id가 123인지 검사 (12 ms)

Test Suites: 1 passed, 1 total
Tests:       2 passed, 2 total
Snapshots:   1 passed, 1 total
Time:        0.263 s, estimated 1 s
Ran all test suites related to changed files.

# if id = 200
FAIL  src/Container.test.js
  ✕ id 값 테스트 (3 ms)
  ✕ id가 123인지 검사 (13 ms)

  ● id 값 테스트

    expect(received).toMatchSnapshot()

    Snapshot name: `id 값 테스트 1`

    - Snapshot  - 1
    + Received  + 1

      <Container
    -   id={123}
    +   id={200}
      />

       6 | test("id 값 테스트", () => {
       7 |   const el = <Container id={id} />;
    >  8 |   expect(el).toMatchSnapshot();
         |              ^
       9 | });
      10 |
      11 | test("id가 123인지 검사", () => {

      at Object.<anonymous> (src/Container.test.js:8:14)

  ● id가 123인지 검사

    TestingLibraryElementError: Unable to find an element with the text: /123/i. This could be because the text is broken up by multiple elements. In this case, you can provide a function for your text matcher to make your matcher more flexible.

    Ignored nodes: comments, script, style
    <body>
      <div>
        <h1>
          200
        </h1>
      </div>
    </body>

      11 | test("id가 123인지 검사", () => {
      12 |   render(<Container id={id} />);
    > 13 |   const containerElement = screen.getByText(/123/i);
         |                                   ^
      14 |   expect(containerElement).toBeInTheDocument();
      15 | });
      16 |

      at Object.getElementError (node_modules/@testing-library/dom/dist/config.js:40:19)
      at Object.<anonymous> (src/Container.test.js:13:35)

 › 1 snapshot failed.
Snapshot Summary
 › 1 snapshot failed from 1 test suite. Inspect your code changes or press `u` to update them.

Test Suites: 1 failed, 1 total
Tests:       2 failed, 2 total
Snapshots:   1 failed, 1 total
Time:        0.47 s, estimated 1 s
Ran all test suites related to changed files.

두 번째 테스트에서 id 값 테스트가 값 200이 입력되었음에도 실패하게 되는 이유는 첫 번째 테스트에서 Snapshot이 123으로 입력되었기 때문이다. src 디렉터리에 snapshots가 생성되어 있는 것을 볼 수 있는데, 이 곳에 스냅샷이 업데이트 되어 있다.

// Container.test.js.snap

// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`id 값 테스트 1`] = `
<Container
  id={123}
/>
`;

id 값이 123으로 기록되었기 때문에 이외에 다른 id 값이 입력되면 실패했음을 알린 것이다.

 › 1 snapshot failed from 1 test suite. Inspect your code changes or press `u` to update them.

따라서 새로 입력한 값을 스냅샷에 업데이트 하고 싶으면 u 키를 눌러 반영하거나, 계속 테스트를 진행하게끔 선택할 수 있다.

사용자의 판단에 따라 스냅샷을 업데이트하며 테스트를 진행하면 될 것 같다.


Reference

An Async Example · Jest

[Jest] Jest 기초

Jest 강좌 | 코딩앙마

초심자를 위한 React Testing Library

react-testing-library 를 사용한 리액트 컴포넌트 테스트

Written by

yhuj79

🌱 Junior Developer