(React)Jest + React Testing Library를 이용하여 테스트 코드 도입하기

kidstone·2024년 4월 20일
0

외개인 프로젝트

목록 보기
2/10

문제상황

필터 기능 구현 중에 다중 선택, 조건부 선택 등 로직이 복잡하여, 이 기능이 제대로 동작하는지 확인하는 작업이 필요하다고 생각했다. 그래서 테스트 전략에 관하여 공부도 할겸 테스트 코드를 도입하기로 결정했다.

Jest + React Testing Library

현재 진행중인 프로젝트는 create-react-app으로 프로젝트를 구성했기 때문에 기본적으로 jestreact testing library가 설치되어 있다. npm test 명령어로 테스트 코드를 실행할 수 있다.

Jest

Jest는 Facebook에서 개발한 JavaScript 테스팅 프레임워크로, 큰 설정 없이 바로 사용할 수 있는 것이 가장 큰 장점이다. React 애플리케이션뿐만 아니라, 모든 JavaScript 프로젝트에 사용할 수 있다. Jest는 빠른 성능, 스냅샷 테스팅, 목(mock) 함수 등 다양한 기능을 제공하여 테스트 과정을 간소화한다.

React Testing Library

React Testing Library는 리액트 컴포넌트를 테스트하기 위해 만들어진 라이브러리다. 이 라이브러리의 핵심 철학은 "더 적은 코드로 더 많은 것을 테스트"하는 것이다. React Testing Library는 DOM에서 컴포넌트를 렌더링하고, 사용자가 실제로 앱을 사용하는 방식대로 테스트를 진행한다. 즉, 구현 세부 사항보다는 앱의 기능적인 측면에 초점을 맞춘다.

Jest + React Testing Library

Jest와 React Testing Library를 함께 사용하면, 리액트 컴포넌트의 단위 테스트와 통합 테스트를 매우 효율적으로 진행할 수 있다. Jest가 테스트 환경을 제공하고, React Testing Library는 사용자 중심의 테스트 작성을 도와준다.

(아래는 vitest를 이용하여 작성한 테스트 코드입니다. vitest를 사용한 이유는 다음 포스팅에서...)

import { fireEvent, render, screen } from '@testing-library/react';
import RoommateFilterPage from './RoommateFilterPage';
import { expect, test, describe } from "vitest";
import '@testing-library/jest-dom';


describe('YourComponent', () => {
  test('사용자가 인원을 선택할 때 상태가 올바르게 업데이트되어야 한다', () => {
    render(<RoommateFilterPage />);
    
    // '2인'과 '3인' 선택
    fireEvent.click(screen.getByTestId('2인'));
    fireEvent.click(screen.getByTestId('3인'));

    // '2인'과 '3인'이 선택되었는지 확인
    expect(screen.getByTestId('2인')).toBeChecked();
    expect(screen.getByTestId('3인')).toBeChecked();

    // '2인' 선택 해제
    fireEvent.click(screen.getByTestId('2인'));

    // '2인'이 선택 해제되었는지 확인
    expect(screen.getByTestId('2인')).not.toBeChecked();
    // '3인'은 여전히 선택되어 있는지 확인
    expect(screen.getByTestId('3인')).toBeChecked();
  });
  
  test('남성을 선택하면 A동과 E동만 선택 가능해야 한다', () => {
    render(<RoommateFilterPage />);

    // 남성 선택
    fireEvent.click(screen.getByLabelText('남성'));
    
    // A동과 E동이 선택 가능한지 확인
    expect(screen.getByLabelText('A동')).not.toBeDisabled();
    expect(screen.getByLabelText('E동')).not.toBeDisabled();

    // B동, C동, D동이 선택 불가능한지 확인
    expect(screen.getByLabelText('B동')).toBeDisabled();
    expect(screen.getByLabelText('C동')).toBeDisabled();
    expect(screen.getByLabelText('D동')).toBeDisabled();
  });
  
  test('남성을 선택하고 E동을 선택하면 호실 유형 선택이 가능해야 한다', () => {
    render(<RoommateFilterPage />);

    // 남성 선택
    fireEvent.click(screen.getByLabelText('남성'));

    // E동 선택
    fireEvent.click(screen.getByLabelText('E동'));

    // 호실 유형 체크박스가 렌더링되었는지 확인
    expect(screen.getByLabelText('2인실')).toBeInTheDocument();
    expect(screen.getByLabelText('4인실')).toBeInTheDocument();

    // 호실 유형 체크박스가 선택 가능한지 확인
    expect(screen.getByLabelText('2인실')).not.toBeDisabled();
    expect(screen.getByLabelText('4인실')).not.toBeDisabled();
  });
  
  test('남성을 선택하고 A, E동을 선택했다가 여자를 선택하면 A,E 동의 선택이 해제된다', () => {
    render(<RoommateFilterPage />);

    // 남성 선택
    fireEvent.click(screen.getByLabelText('남성'));

    // A, E동 선택
    fireEvent.click(screen.getByLabelText('E동'));
    fireEvent.click(screen.getByLabelText('A동'));

    // 기숙사 동 체크박스가 체크되었는지 확인
    expect(screen.getByLabelText('E동')).toBeChecked();
    expect(screen.getByLabelText('A동')).toBeChecked();

    expect(screen.getByLabelText('B동')).toBeDisabled();
    expect(screen.getByLabelText('C동')).toBeDisabled();
    expect(screen.getByLabelText('D동')).toBeDisabled();

    //여성 선택
    fireEvent.click(screen.getByLabelText('여성'));

    // 기숙사 동 체크박스가 선택 가능한지 확인
    expect(screen.getByLabelText('E동')).not.toBeChecked();
    expect(screen.getByLabelText('A동')).not.toBeChecked();
    expect(screen.getByLabelText('E동')).toBeDisabled();
    expect(screen.getByLabelText('A동')).toBeDisabled();

    expect(screen.getByLabelText('B동')).not.toBeDisabled();
    expect(screen.getByLabelText('C동')).not.toBeDisabled();
    expect(screen.getByLabelText('D동')).not.toBeDisabled();
  });
});

decribe를 통해 여러 테스트 케이스를 묶고, test(it)안에 개별 테스트 케이스를 작성한다.
@testing-library/react에서 불러온 render 함수를 이용하여 테스트할 컴포넌트를 렌더링한 후, screen 객체를 통해 렌더링된 요소들에 접근할 수 있다. fireEvent 객체는 React Testing Library에서 제공하는 유틸리티 함수 중 하나로 테스트 중에 이벤트를 발생시키는 데 사용한다.
expect 함수 안에는 테스트 할 값을 넣고, 이를 matcher(toBeDisabled 등)를 통해 일치하는지의 여부를 확인한다.

한계

테스트 코드를 잘 작성하는 것도 중요하겠지만, 우선적으로 무엇을 테스트할지를 정하는 것이 더욱 중요하다는 생각이 들었다. 모든 UI 전환이나 작은 기능에 대해서 전부 케이스를 작성하여 테스트 코드를 작성하는 것은 매우 비효율적일 것이다. 이 프로젝트는 TDD기반으로 개발하는 것이 아니기 때문에 기준을 잘 설정하여 테스트 코드를 작성해나가며 더욱 완전한 기능 개발을 이어나갈 예정이다.
+

msw를 이용하여 api 모킹 테스트를 진행했을 때 여러가지 문제점에 직면하였다. 이는 다음 포스팅에서 풀어볼 예정이다.

profile
안녕하세요. 웹 프론트엔드 개발자 앞잡이 '꼬마돌' 입니다.

0개의 댓글