기능은 돌아가는 기존의 소중한 쓰레기(?) 코드들을 리팩토링하고 싶다는 생각이 들었다.
그런데 리팩토링을 하다보니까 고칠 때마다 돌아가는 지 모든 경우를 확인하는 게 귀찮기도 하고 복잡하기도 했다.
이러한 문제를 해결하기 위해 테스트 코드를 작성해보려고 한다.
테스트 코드를 잘 작성해놓으면 이 테스트코드 통과하는 것만으로도 기능이 잘 돌아간다는 게 보장되기 때문이다 !!
현재 알고 있는 테스트 방법로는 테스트의 범위에 따라 3가지가 있다.
- unit test (함수가 잘 동작하는 지 테스트. 작은 단위)
- integration test (다른 컴포넌트 또는 시스템과의 상호작용 테스트)
- e2e test (End-To-End test) (실제 사용자 시나리오 테스트)
리액트 컴포넌트 테스트도 테스트하는 범위에 따라 범주가 결정된다. 하나의 컴포넌트 안에서 props가 잘 들어오는 지 테스트하면 unit test
, 다른 컴포넌트와의 상호작용, API 등을 통합해서 테스트하면 integration test
로 부른다.
RTL에서는 유저 중심으로, 구현보다는 유저가 보는 브라우저에 메시지가 노출이 되는지 파악하는 것을 중요시한다고 한다.
따라서, 컴포넌트 테스트에서 테스트해야 할 것은 다음과 같다.
- 컴포넌트에 props가 정상적으로 들어오는 지
- 상태 변화에 따라 컴포넌트가 어떻게 변하는 지
- 컴포넌트가 유저의 인터랙션에 따라 어떻게 변하는 지
모든 컴포넌트 테스트는 다음 순서에 따라 구조적으로 진행된다.
- 컴포넌트를 렌더링한다.
- 컴포넌트에서 요소를 찾거나 유저 인터랙션을 시뮬레이션한다.
- 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 하나를 짜면서 느낀점은 "테스트 코드를 작성하기 쉬운 코드"라고 한다면 다른 데이터와 의존성이 적으며, 단일 책임 원칙을 지킨 컴포넌트라는 생각이 든다.
앞으로는 테스트하기 쉬운 컴포넌트에 대해 고민하면서 구현해봐야겠다 !