[TEST] component 테스트 코드로 편하게 리팩토링하기

Gyuhan Park·2023년 9월 2일
0

nextjs

목록 보기
7/8
post-custom-banner

💭 TMI

기능은 돌아가는 기존의 소중한 쓰레기(?) 코드들을 리팩토링하고 싶다는 생각이 들었다.
그런데 리팩토링을 하다보니까 고칠 때마다 돌아가는 지 모든 경우를 확인하는 게 귀찮기도 하고 복잡하기도 했다.
이러한 문제를 해결하기 위해 테스트 코드를 작성해보려고 한다.
테스트 코드를 잘 작성해놓으면 이 테스트코드 통과하는 것만으로도 기능이 잘 돌아간다는 게 보장되기 때문이다 !!

🔎 테스트 방법

현재 알고 있는 테스트 방법로는 테스트의 범위에 따라 3가지가 있다.

  1. unit test (함수가 잘 동작하는 지 테스트. 작은 단위)
  2. integration test (다른 컴포넌트 또는 시스템과의 상호작용 테스트)
  3. e2e test (End-To-End test) (실제 사용자 시나리오 테스트)

리액트 컴포넌트 테스트도 테스트하는 범위에 따라 범주가 결정된다. 하나의 컴포넌트 안에서 props가 잘 들어오는 지 테스트하면 unit test , 다른 컴포넌트와의 상호작용, API 등을 통합해서 테스트하면 integration test 로 부른다.

🧱 컴포넌트 테스트

RTL에서는 유저 중심으로, 구현보다는 유저가 보는 브라우저에 메시지가 노출이 되는지 파악하는 것을 중요시한다고 한다.

따라서, 컴포넌트 테스트에서 테스트해야 할 것은 다음과 같다.

  1. 컴포넌트에 props가 정상적으로 들어오는 지
  2. 상태 변화에 따라 컴포넌트가 어떻게 변하는 지
  3. 컴포넌트가 유저의 인터랙션에 따라 어떻게 변하는 지

모든 컴포넌트 테스트는 다음 순서에 따라 구조적으로 진행된다.

  1. 컴포넌트를 렌더링한다.
  2. 컴포넌트에서 요소를 찾거나 유저 인터랙션을 시뮬레이션한다.
  3. assertion을 작성한다.
import PlaceInfoSection from '@detail/PlaceInfoSection';
import { render, screen } from '@testing-library/react';
import { IFacilityMarker } from 'types/map';

const initialSelectedPlace: IFacilityMarker = {
  address: '서울 종로구 효자로12',
  closing_time: '18:00',
  contact: '00-0000-0000',
  id: 1,
  imageSrc: 'https://wheelpass.s3.ap-northeast-2.amazonaws.com/gomuseum.jpg',
  latitude: '37.5765513',
  longitude: '126.9756893',
  name: '국립고궁박물관',
  opening_time: '10:00',
  type: 'museum'
};

jest.mock('next/router', () => ({
  useRouter: () => ({
    query: { result: '국립고궁박물관' },
    asPath: '/navigation?result=국립고궁박물관'
  })
}));

jest.mock('@apis/map', () => ({
  // api mock 함수 및 Promise 반환 설정
  getDetailFacility: jest.fn(() => Promise.resolve(initialSelectedPlace))
}));

describe('PlaceInfoSection 컴포넌트 테스트', () => {
  it('PlaceInfoSection test', () => {
    // 1. 컴포넌트를 렌더링한다.
    render(
      <PlaceInfoSection selectedPlace={initialSelectedPlace} setSelectedPlace={() => jest.fn()} />
    );
	// 2. 컴포넌트에서 요소를 찾거나 유저 인터랙션을 시뮬레이션한다.
    const name = screen.getByText('국립고궁박물관');
    const address = screen.getByText('서울 종로구 효자로12');

	// 3. assertion을 작성한다.
    expect(require('@apis/map').getDetailFacility).toHaveBeenCalledWith('국립고궁박물관');
    expect(name).toBeInTheDocument();
    expect(address).toBeInTheDocument();
  });
});

😎 느낀점

테스트 코드 하나 짜는데 많은 오류가 있었고 많은 시간이 걸렸다 ㅋㅋㅋㅋㅋㅋㅋㅌ
내가 부족해서 그런거긴 한데 다른 데이터에 의존성이 높은 컴포넌트일수록 테스트 코드 또한 더 복잡성을 띄는 것 같다.

컴포넌트 안에서 필요한 외부 데이터들을 주입해야하는 부분에서 애를 먹었는데 실제 API를 사용하지 않고 API를 mocking하여 실제로 동작하는 것처럼 테스트를 진행할 수 있었다.

TC 하나를 짜면서 느낀점은 "테스트 코드를 작성하기 쉬운 코드"라고 한다면 다른 데이터와 의존성이 적으며, 단일 책임 원칙을 지킨 컴포넌트라는 생각이 든다.

앞으로는 테스트하기 쉬운 컴포넌트에 대해 고민하면서 구현해봐야겠다 !

테스트 코드 종류
컴포넌트 테스트 코드 작성하는 법

profile
단단한 프론트엔드 개발자가 되고 싶은
post-custom-banner

0개의 댓글