본문 바로가기
Testing

[Jest #4] 리액트 컴포넌트 + 스냅샷 테스트

by 쾌횽 2023. 7. 22.
반응형

지금까지 익혔던 Jest를 React에서 활요해 보자.

CRA로 프로젝트를 하나 만들고 간단한 컴포넌트를 작성한다.

// Hello.js

import React from "react";

const Hello = () => {
  return <h1>Hello</h1>;
};

export default Hello;

App컴포넌트에 넣어준다.

// App.js

import "./App.css";
import Hello from "./components/Hello";

function App() {
  return (
    <div className='App'>
      <Hello />
    </div>
  );
}

export default App;

App컴포넌트에 user객체를 하나 만들고 Props로 전달해 준다.

// App.js

import "./App.css";
import Hello from "./components/Hello";

const user = {
  name: "Mike",
  age: 30,
};

function App() {
  return (
    <div className='App'>
      <Hello user={user} />
    </div>
  );
}

export default App;

전달받은 Props를 활용해 user의 name이 존재하면 Hello! user.name을 보여주고 없으면 로그인 버튼을 보여준다.

// Hello.js

import React from "react";

const Hello = ({ user }) => {
  return user.name ? <h1>Hello! {user.name}</h1> : <button>Login</button>;
};

export default Hello;

테스트를 진행 해 보자.

CRA로 빌드된 리액트 프로젝는 Jest가 설치되어있어, 별도의 설치 과정이 필요없다.

Hello.test.js파일을 하나 만들고 테스트 코드를 만든다.

리액트에서 테스팅 라이브러리 render와 screen을 가져온다.

//Hello.test.js

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

const user = {
  name: "Mike",
  age: 30,
};

test("Hello 라는 글자가 포함되는가?", () => {
  render(<Hello user={user} />);
  const helloEl = screen.getByText(/Hello/i);
  expect(helloEl).toBeInTheDocument();
});

render를 통해 컴포넌트를 가져오고 user를 Props로 전달했다. 

screen으로 통해서 Hello라는 글자가있는지 정규표현식으로 나타내고 toBeInTheDocument()를 통해 document에있는지 확인한다.

테스트를 돌려보면 잘 통과되는 것을 볼 수 있다.

 

스냅샷

다음은 스냅샷을 활요해보자.

스냅샷은 성공하는 케이스를 찍어두고 비교하면서 테스트하는 방식이다.

렌더링된 화면과 찍어둔 화면이 다르면 실패한다.

// Hello.test.js

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

const user = {
  name: "Mike",
  age: 30,
};

const user2 = {
  age: 20,
};

test("snapshot : name있음", () => {
  const view = render(<Hello user={user} />);
  expect(view).toMatchSnapshot();
});

test("snapshot : name없음", () => {
  const view = render(<Hello user={user2} />);
  expect(view).toMatchSnapshot();
});

test("Hello 라는 글자가 포함되는가?", () => {
  render(<Hello user={user} />);
  const helloEl = screen.getByText(/Hello/i);
  expect(helloEl).toBeInTheDocument();
});

이름이 있는 객체와 없는 객체를 각각 스냅샷을 찍어보면, componenet파일에 __snapshot__이라는 폴더가 생겼을 것이다.

snap파일에 들어가보면 내가 작성한 코드가 적혀있다. 여기에서 조금의 수정 사항이 생기면 테스트는 실패하게 된다.

// Hello.test.js

...
const user = {
  name: "Tom",
  age: 30,
};
...

user의 이름을 Tom으로 수정하서 테스트를 진행 해 보면,

 FAIL  src/components/Hello.test.js
  ✕ snapshot : name있음 (26 ms)
  ✓ snapshot : name없음 (9 ms)
  ✓ Hello 라는 글자가 포함되는가? (19 ms)

  ● snapshot : name있음

    expect(received).toMatchSnapshot()

    Snapshot name: `snapshot : name있음 1`

    - Snapshot  - 2
    + Received  + 2

    @@ -2,18 +2,18 @@
        "asFragment": [Function],
        "baseElement": <body>
          <div>
            <h1>
              Hello! 
    -         Mike
    +         Tom
            </h1>
          </div>
        </body>,
        "container": <div>
          <h1>
            Hello! 
    -       Mike
    +       Tom
          </h1>
        </div>,
        "debug": [Function],
        "findAllByAltText": [Function],
        "findAllByDisplayValue": [Function],

당연히 실패하게 된다. 실패했을 경우 우리는 선택할 수 있다.

첫번째는 스냅샷을 업데이트 해 주는 것이다.

바뀐 수정 사항이 최신의 코드라고 알려주고, 비교 코드로 설정하는 방식이다.

키보드 u를 누르면 간단하게 업데이트를 한다.

하지만 버그가 존재해서 테스트에 실패했는데 꼼꼼히 살펴보지 않고  업데이트를 진행하게되면 이후부터는 버그가 있는 상태에서 테스트가 통과되는 일이 발생한다. 그렇기에 업데이트를 진행할때는 신중하게 업데이트를 진행해야한다. 

 

두번째는 당연하게도 버그를 수정하는것이다.


새롭게 시간에 따른 컴포넌트를 하나 만들어 준다. 

// App.js

import "./App.css";
import Timer from "./components/Timer";

function App() {
  return (
    <div className='App'>
      <Timer />
    </div>
  );
}

export default App;

Timer컴포넌트

// Timer.js

export default function Timer() {
  const now = Date.now();
  const sec = new Date(now).getSeconds();
  return <p>현재 {sec}초 입니다.</p>;
}

진입 시점의 초를 보여주기 때문에 새로고침할 때 마다 시간이 바뀐다.

test파일을 만들어주고 test를 진행해주면 테스트는 통과하고 1개의 스냅샷이 생기게 된다.

// Timer.test.js

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

test("초 표시", () => {
  const view = render(<Timer />);
  expect(view).toMatchSnapshot();
});

스냅샷에서 보여지는 현제 초는 24초이다.

테스트를 다시 돌려보면, 실패하게 된다.

이렇게 계속 바뀌는 값에 대해 테스트를 진행하면 테스트는 항상 실패하게 된다. 이럴 때는 어떻게 테스트 해야할까?

바로 Mock함수를 이용하면 된다.

Mock함수는 테스트를 하기 위해 흉내만 내는 함수라고 생각하면 좋겠다.

jest.fn()를 이용한다.

// Tomer.test.js

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

test("초 표시", () => {
  Date.now = jest.fn(() => 123456789);
  const view = render(<Timer />);
  expect(view).toMatchSnapshot();
});

mock함수를 만들어주고 다시 테스트를 돌리면 기존의 스냅샷과 다르기때문에 실패가 나온다.

키보드 u를 눌러 업데이트를 진행 시켜주고 다시 테스트를 돌려봐도 항상 통과가 나온다.

테스트는 언제나 123456789의 숫자로 진행 되기 때문에 항상 통과가 나온다.

이처럼 시간에 따라서 변할 수 있는 것들은 테스트 전에 mock함수를 통해서 고정된 값으로 바꿔주면 해결가능하다.

 


참고자료

반응형

댓글