CRA에서 테스트 코드 작성하기

rloo8·2024년 5월 27일
post-thumbnail

앞서 테스트 코드의 중요성을 알아보았다.

이번 글에서는 직접 테스트 코드를 작성해보자!

React Testing Library와 Jest

React에서 테스트 코드를 작성하기 위해 여러 라이브러리와 프레임워크가 있지만,
이 글에서는 React Testing LibraryJest를 사용했다.

React Testing Library (RTL)

  • 컴포넌트 렌더링: 가상 DOM에 React 컴포넌트를 렌더링한다.
  • 사용자 관점의 테스트: 사용자 행동을 시뮬레이션하여 테스트를 작성한다. (예: 클릭, 입력 등)
  • 쿼리 함수 제공: getByText, getByRole 등 다양한 쿼리 함수를 제공하여 DOM 요소를 쉽게 선택할 수 있다.

Jest

  • 테스트 러너: 테스트를 실행하고 테스트 결과를 리포팅한다.
  • 어서션 라이브러리: expect와 같은 함수를 제공하여 테스트 결과를 검증한다.
  • 모킹(mocking): 함수나 모듈을 모킹하여 테스트 시 의존성을 제어할 수 있다.

모킹이란?
모킹은 실제 객체 대신 사용할 가짜 객체나 함수를 만드는 기술을 말한다.
예를 들어, axios 호출이 필요한 경우 실제 API를 호출하는 대신 가상의 응답을 반환하는 목업 함수를 만들어 테스트를 진행한다.


CRA(create-react-app)를 이용해서 React 프로젝트를 생성하면
자동으로 React Testing Library와 Jest가 포함되어 있는 환경을 구축할 수 있다.

실습

간단한 카운터 앱을 만들면서 테스트 코드를 작성해 볼 것이다.

요구사항

  • CountButtons 컴포넌트: +버튼과 -버튼이 있고 각각 버튼을 누르면 증가, 감소 함수가 실행되는 컴포넌트
  • App 컴포넌트: 컴포넌트 렌더링 및 카운트 로직을 담당

컴포넌트 생성 및 렌더링

CountButtons.tsx

function CountButtons() {
  return (
    <div>
      <button>+</button>
      <button>-</button>
    </div>
  );
}
export default CountButtons;

App.tsx

import React from 'react';
import CountButtons from './Counter/CountButtons';

function App() {
  return (
    <div>
      <h1>현재 숫자: 0</h1>
      <CountButtons />
    </div>
  );
}

export default App;

위와 같이 컴포넌트를 작성해주고 실행시키면 아래와 같은 화면을 확인할 수 있다.


렌더링 테스트

App 컴포넌트에서 각각의 컴포넌트가 잘 렌더링 되고 있는지 테스트해보자.

CRA로 프로젝트를 생성했을 때 기본으로 있는 App.test.tsx 파일에 테스트 코드를 작성해준다.

App.test.tsx

import { render, screen } from '@testing-library/react';
import '@testing-library/jest-dom'; // Jest의 확장 기능
import App from './App';

describe('App 컴포넌트', () => {
  it('화면 및 컴포넌트 렌더링', () => {
	// RTL를 사용하여 컴포넌트 렌더링
    render(<App />);
    
    // RTL의 쿼리 함수 사용
    const count = screen.getByText('현재 숫자: 0');
    const buttons = screen.getAllByRole('button');
		
	// 테스트 검증
    expect(count).toBeInTheDocument(); // toBeInTheDocument: DOM에 존재하는지 검증
    expect(buttons.length).toBe(2); // toBe: 기대하는 값과 일치하는지 검증
  });
});
  • describe : 테스트들을 그룹화 하는 역할을 한다.

  • it : 하나의 테스트를 정의한다.

  • render : 테스트하고자 하는 컴포넌트를 렌더링한다.

  • screen : DOM요소에 접근하고 검증하는 데 사용되는 객체이다. 다양한 테스트 쿼리 함수들을 반환한다.

  • expect : 테스트 검증을 수행한다.

테스트 코드 작성 후 npm test 로 테스트를 실행시키면 터미널에서 테스트 통과를 확인할 수 있다.


함수 호출 테스트

이번에는 +버튼과 -버튼을 클릭했을 때 함수가 호출되는 것을 테스트해보자.

CountButtons.tsx

CountButtons 컴포넌트가 증가, 감소 함수를 전달하도록 수정한 후, 해당 함수를 click 이벤트에 걸어준다.
또 렌더링 테스트를 위해 data-testid를 추가해준다.

type FnType = {
  incrementFn: () => void;
  decrementFn: () => void;
};

function CountButtons({ incrementFn, decrementFn }: FnType) {
  return (
    <div>
      <button onClick={incrementFn} data-testid="incrementBtn">
        +
      </button>
      <button onClick={decrementFn} data-testid="decrementBtn">
        -
      </button>
    </div>
  );
}
export default CountButtons;

CountButtons.test.tsx

import { fireEvent, render, screen } from '@testing-library/react';
import '@testing-library/jest-dom';
import CountButtons from './CountButtons';

// mock 함수 생성
const incrementFn = jest.fn();
const decrementFn = jest.fn();

describe('CountButtons 컴포넌트', () => {
// 버튼 렌더링 테스트
  it('+버튼, -버튼 렌더링', () => {
	// mock 함수를 props로 전달
    render(<CountButtons incrementFn={incrementFn} decrementFn={decrementFn} />);
    
    // data-testid 요소 선택
    const incrementBtn = screen.getByTestId('incrementBtn');
    const decrementBtn = screen.getByTestId('decrementBtn');

    expect(incrementBtn).toBeInTheDocument();
    expect(decrementBtn).toBeInTheDocument();
  });
	
// 함수 호출 테스트
  it('증가, 감소 함수 호출', () => {
    render(<CountButtons incrementFn={incrementFn} decrementFn={decrementFn} />);

    const incrementBtn = screen.getByTestId('incrementBtn');
    const decrementBtn = screen.getByTestId('decrementBtn');
		
	// 버튼 클릭 이벤트 발생
    fireEvent.click(incrementBtn);
    fireEvent.click(decrementBtn);
		
	// 함수가 호출되었는지 검증
    expect(incrementFn).toBeCalled();
    expect(decrementFn).toBeCalled();
  });
});
  • jest.fn() : Jest에서 제공하는 모킹 함수
  • fireEvent : 특정 요소에 이벤트를 발생시킨다.

테스트가 통과된 것을 확인할 수 있다.
이때 Test Suites는 describe로 묶은 테스트 묶음의 개수이고, Tests는 각각의 테스트 개수를 나타낸다.


클릭 이벤트 구현

App.tsx

App 컴포넌트를 다시 확인하면 CountButtons 컴포넌트에 props를 전달하지 않아 오류가 난 것을 확인할 수 있다.

실제 증가, 감소 함수를 구현하여 처리해준다.

import React, { useState } from 'react';
import CountButtons from './Counter/CountButtons';

function App() {
  const [count, setCount] = useState(0);

  const handleIncrement = () => {
    setCount((count) => count + 1);
  };
  const handleDecrement = () => {
    setCount((count) => count - 1);
  };

  return (
    <div>
      <h1>현재 숫자: {count}</h1>
      <CountButtons incrementFn={handleIncrement} decrementFn={handleDecrement} />
    </div>
  );
}

export default App;

클릭 이벤트 테스트

이제 구현한 함수가 제대로 작동하고 있는지 테스트 해보자.

App.test.tsx

it('+버튼 클릭 시 1씩 증가', () => {
  render(<App />);

  const incrementBtn = screen.getByTestId('incrementBtn');
  fireEvent.click(incrementBtn);
  fireEvent.click(incrementBtn);

  const changeScreen = screen.getByText('현재 숫자: 2');
  expect(changeScreen).toBeInTheDocument();
});

it('-버튼 클릭 시 1씩 감소', () => {
  render(<App />);

  const decrementBtn = screen.getByTestId('decrementBtn');
  fireEvent.click(decrementBtn);
  fireEvent.click(decrementBtn);

  const changeScreen = screen.getByText('현재 숫자: -2');
  expect(changeScreen).toBeInTheDocument();
});

테스트가 모두 통과되었다.


앱 실행

npm start 로 앱을 실행해보면 요구사항에 맞게 앱이 작동하는 것을 확인할 수 있다.


+ Trouble Shooting

CRA를 통해 프로젝트를 생성하고 npm test로 테스트를 실행했을 때,
render 함수에서 아래와 같은 경고가 떴다.

console.error
      Warning: `ReactDOMTestUtils.act` is deprecated in favor of `React.act`. 
      Import `act` from `react` instead of `react-dom/test-utils`.
      See https://react.dev/warnings/react-dom-test-utils for more info.

기본으로 생성되는 App.test.tsx 파일을 수정하지 않고 그대로 테스트를 실행해도 같은 경고가 나왔기 때문에
테스트 코드의 문제는 아니었다.

콘솔창에서 알려준 공식문서 페이지에 들어가보니 React Testing Library가 업데이트되면서 더 이상 권장되지 않는 버전을 사용하고 있어 경고가 뜬 것이다.

해결 방법

npm install react@latest react-dom@latest @testing-library/react@latest

React와 React Testing Library를 최신 버전으로 업데이트한 후, 다시 실행해보니 해당 경고가 사라졌다.

CRA로 프로젝트를 생성할때 항상 최신 버전의 패키지가 설치되지는 않는가보다. 버전 확인을 잘하자!

0개의 댓글