
앞서 테스트 코드의 중요성을 알아보았다.
이번 글에서는 직접 테스트 코드를 작성해보자!
React에서 테스트 코드를 작성하기 위해 여러 라이브러리와 프레임워크가 있지만,
이 글에서는 React Testing Library와 Jest를 사용했다.
getByText, getByRole 등 다양한 쿼리 함수를 제공하여 DOM 요소를 쉽게 선택할 수 있다.expect와 같은 함수를 제공하여 테스트 결과를 검증한다.모킹이란?
모킹은 실제 객체 대신 사용할 가짜 객체나 함수를 만드는 기술을 말한다.
예를 들어, axios 호출이 필요한 경우 실제 API를 호출하는 대신 가상의 응답을 반환하는 목업 함수를 만들어 테스트를 진행한다.
CRA(create-react-app)를 이용해서 React 프로젝트를 생성하면
자동으로 React Testing Library와 Jest가 포함되어 있는 환경을 구축할 수 있다.
간단한 카운터 앱을 만들면서 테스트 코드를 작성해 볼 것이다.
요구사항
- CountButtons 컴포넌트: +버튼과 -버튼이 있고 각각 버튼을 누르면 증가, 감소 함수가 실행되는 컴포넌트
- App 컴포넌트: 컴포넌트 렌더링 및 카운트 로직을 담당
function CountButtons() {
return (
<div>
<button>+</button>
<button>-</button>
</div>
);
}
export default CountButtons;
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 파일에 테스트 코드를 작성해준다.
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 컴포넌트가 증가, 감소 함수를 전달하도록 수정한 후, 해당 함수를 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;
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 컴포넌트를 다시 확인하면 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;
이제 구현한 함수가 제대로 작동하고 있는지 테스트 해보자.
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 로 앱을 실행해보면 요구사항에 맞게 앱이 작동하는 것을 확인할 수 있다.
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로 프로젝트를 생성할때 항상 최신 버전의 패키지가 설치되지는 않는가보다. 버전 확인을 잘하자!