Graphy | Front-end Test

Kyu-hyun Lee
10 min readJun 10, 2023

--

저는 Graphy 프로젝트를 진행하며 React Testing Library와 Jest로 테스트 코드를 작성하여 React Component에 대해 Unit Test를 진행하였는데요, 이 글을 통해 왜 React Testing Library와 Jest를 선택하였고 어떻게 테스트 코드를 짰는지 공유해보려고 합니다.

React Testing Library

React-Testing-Library Logo

React Testing Library 라이브러리는 테스트 방법론 중 하나인 Behavior Driven Test(행위 주도 테스트)의 접근 방식을 채택하고 있어, 사용자 경험에 더욱 초점을 맞춘 테스트 작성이 가능합니다.

Behavior Driven Test는 사용자의 애플리케이션 이용 경험에 집중하는 방식으로, 이를 통해 보다 실질적인 사용자 경험에 가까운 테스트를 가능하게 합니다. 예를 들어, <h2 class="title">제목</h2>과 같은 UI 요소를 테스트할 때, 기존의 Implementation Driven Test(구현 주도 테스트)는 <h2> 태그와 title 클래스의 존재에 주로 집중했습니다.

그러나 React Testing Library와 같은 Behavior Driven Test 접근법을 사용하면, 이런 내부 구현 사항보다는 사용자가 어떤 컨텐츠를 보고, 그들의 행동에 따라 애플리케이션의 반응을 어떻게 테스트할 것인지에 초점이 맞춰집니다.

즉, 사용자의 입장에서는 제목이라는 텍스트가 여전히 화면에 보여진다는 사실이 중요하고, 이것이 <h2> 태그인지, <h3> 태그인지는 그렇게 중요하지 않습니다. 따라서 <h2>에서 <h3>로 변경되더라도 Behavior Driven Test 방법론을 사용한 테스트는 문제없이 통과하게 됩니다.

React Testing Library의 이런 특징은 개발자들이 컴포넌트의 내부 구조나 동작 방식에 대한 지식 없이도 테스트를 작성할 수 있게 해주며, 이는 테스트의 유지보수성과 가독성을 크게 향상시킵니다. 또한, 이러한 방식으로 작성된 테스트는 애플리케이션의 실제 동작을 더 정확하게 반영할 수 있게 됩니다. 그래서 이런 장점들 덕분에 React Testing Library로 UnitTest를 진행하게 되었습니다.

Jest

Jest Logo

React Testing Library로 테스트 코드를 작성해도, 테스트 환경이 없다면 실행할 수 없습니다. 그렇기에 테스트 환경을 제공해주는 라이브러리를 찾았고 저는 Jest를 선택하게 되었습니다. Jest는 JavaScript 테스트 프레임워크로, 가벼우면서도 강력한 테스트 환경을 제공합니다. Jest는 속도가 빠르고, 병렬 테스트 실행, 스냅샷 테스팅, 타이머 모킹 등 다양한 기능을 제공하여 사용자 친화적인 테스트 환경을 만들어 줍니다. 또한, 설정이 간단하고 문서화가 잘 되어 있어, 쉽게 테스트 환경을 구축하고 유지할 수 있습니다. 이러한 이유로 Jest를 테스트 실행 환경으로 선택하게 되었습니다.

테스트를 진행하는 모습

테스트 세팅

처음엔 먼저 jest를 설치합니다. 테스팅 라이브러리이기 때문에 개발 의존성(-D 옵션)으로 설치해야 합니다. (create-react-app으로 생성된 프로젝트는 기본적으로 jest가 내장되어 있으니 스킵해도 됩니다.)

npm i -D jest

다음으로 React Testing Library를 설치합니다. 반드시 react-testing-library가 아닌 @testing-library/react를 설치해야합니다.

npm i -D @testing-library/react

마지막으로 Jest의 DOM에 특화된 matcher 애드온인 jest-dom을 설치합니다. jest-dom은 사용자가 HTML DOM 요소를 보다 쉽게, 그리고 더욱 세밀하게 검사할 수 있는 기능을 제공합니다. jest-dom은 이러한 기능 덕분에 React Testing Library와 함께 자주 사용됩니다.

JestDom의 Custom Macher들은 이 문서에서 확인하세요. https://github.com/testing-library/jest-dom#table-of-contents

npm i -D @testing-library/jest-dom

React Testing Library의 주요 API

React Testing Library에는 크게 DOM에 컴포넌트를 랜더링 해주는 render() 함수와 DOM에서 특정 영역을 선택하기 위한 다양한 쿼리 함수들, 특정 이벤트를 발생시켜주는 fireEvent 객체가 존재합니다.

render() 함수는 인자로 랜더링할 React 컴포넌트를 받습니다. 그리고 React Testing Library에서 제공하는 모든 쿼리 함수와 기타 유틸리티 함수를 담고 있는 객체를 리턴합니다.

그렇기에 다음과 같이 자바스크립트의 객체 Destructuring 문법으로 render() 함수가 리턴한 객체로 부터 원하는 쿼리 함수를 얻어올 수 있습니다.

const { getByText, getByLabelText } = render(
<YourComponent />
);

const 테스트 = getByText('테스트');

한편 screen을 사용하면 render() 함수에서 쿼리 함수를 가져오지 않아도 됩니다.

쿼리 함수의 종류는 아래 문서를 참고해주세요.

https://testing-library.com/docs/dom-testing-library/cheatsheet

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

import YourComponent from '../../components/YourComponent';

test('YourComponent 테스트', () => {
render( //가상 DOM에 YourComponent를 렌더링
<YourComponent />
);

const test = screen.getByText('테스트'); // '테스트'라는 텍스트를 가진 요소를 test로 선언
expect(test).toBeInTheDocument(); // test라는 요소가 있는지 확인
});

이 예제 코드는 YourComponent 컴포넌트 안에 테스트라는 텍스트를 가진 요소를 test라고 선언하고 그게 요소에 있는지 확인하는 코드입니다. render안에 <YourComponent/>를 인자로 넣음으로써 가상 DOM에 YourComponent를 렌더링하고 screen.getByText(’테스트’)를 통해 ‘테스트’라는 텍스트를 가진 요소를 test라고 선언합니다. 마지막으로 Custom Macher인 toBeInTheDocument()로 test라는 요소가 있는지 확인합니다.

fireEvent 객체는 쿼리 함수로 선택된 영역을 대상으로 특정 이벤트를 발생시키기 위한 이벤트 함수들을 담고 있습니다. Click, Change, Drop, KeyDown 등등 다양한 이벤트 함수들이 존재합니다.

const test = screen.getByText('테스트'); // '테스트'라는 텍스트를 가진 요소를 test로 선언
fireEvent.click(test); // test라는 요소를 클릭하는 이벤트를 발생시킴

Component Mocking

컴포넌트 안에는 자식 컴포넌트가 있는 경우가 있습니다. render안에 부모 컴포넌트를 넣으면 자식 컴포넌트도 같이 렌더링이 되는데요, 컴포넌트를 하나하나 테스트하는 UnitTest에서 자식 컴포넌트를 테스트 했는데 또 부모컴포넌트 테스트에서 자식 컴포넌트까지 확인하면 비효율적이겠죠. 그래서 Component Mocking을 사용하게 됩니다.

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

import YourComponent from '../../components/YourComponent';

jest.mock(
'../../components/NavBar',
() =>
function () {
return <div data-testid="NavBar" />;
},
);

test('YourComponent 테스트', () => {
render( //가상 DOM에 YourComponent를 렌더링
<YourComponent />
);
});

위는 ‘../../components/NavBar’를 탈취해서 <div data-testid=”NavBar” />로 바꾸는 코드입니다.

Mock Service Worker로 Server Mocking

MSW(Mock Service Worker)는 서비스 워커(Service Worker)를 사용하여 네트워크 호출을 가로채는 API 모킹(mocking) 라이브러리입니다.

백앤드 API 개발과 프론트앤드 UI 개발이 동시에 진행되야하는 경우, 백앤드 API 구현이 완료될 때까지 프론트앤드 팀에서 임시로 사용하기 위한 가짜(mock) API를 서비스 워커로 돌릴 수 있고 , 테스트를 실행 시 실제 백앤드 API에 네트워크 호출을 하는 대신에 훨씬 빠르고 안정적인 가짜 API 서버를 구축할 수 있습니다.

import { rest } from 'msw';
import { setupServer } from 'msw/node';

const server = setupServer(
rest.get('http://localhost:8080/api', (req, res, ctx) => {
return res(
ctx.json({mockData},
}),
);
}),
);

이를 통해 테스트 코드에서 서버를 모킹해서 가짜 data를 받아 테스트를 진행할 수 있습니다. 단 테스트 코드 위에 이를 붙여야 합니다.

beforeAll(() => server.listen());
afterEach(() => server.resetHandlers());
afterAll(() => server.close());

마치면서

React Testing Library와 Jest를 선택한 이유는 사용자 중심의 테스트 작성과 강력한 테스트 환경을 제공하기 때문입니다. 이러한 도구들을 활용하여 Frontend의 Unit Test를 진행하면 코드의 유지보수성과 가독성을 향상시킬 수 있습니다. 효율적이고 정확한 테스트를 통하면 안정적인 애플리케이션 개발에 도움이 되기에 React Testing Library와 Jest는 Frontend 개발에서 필수적인 도구로 자리잡고 있습니다. 테스트 주도 개발(TDD) 방법론을 적용하여 더 견고하고 신뢰성 있는 코드를 구축하는 데 도움이 되길 바랍니다.

--

--