- Udemy의 React - The Complete Guide 강의의 Section 27: Testing React Apps(Unit Tests)를 듣고 공부한 내용을 간략히 정리.
Automating Testing (테스트 자동화)
테스트는 왜 해야할까?
- 개발자가 개발하는 도중, 수동으로 작동 등을 확인하는 것은 '수동 테스트(Manual Test)'이다.
- 다만, 테스트를 수작업으로 진행할 경우 가능한 모든 테스트 케이스(시나리오)를 테스트할 수 없기 때문에 예상치 못한 에러가 발생할 수 있다.
- 이에 반해, 자동화된 테스트는 모든 시나리오를 자동적으로 테스트할 수 있다. 개발자가 하나하나 손으로 하는 것보다 시간도 절약할 수 있다.
- 따라서 수동 테스트만 진행하는 것 보다, 자동 테스트를 같이 병행함으로써 에러를 조기에 발견하고, 더 좋은 코드를 완성할 확률을 높일 수 있다.
테스트의 종류
1. 유닛 테스트
- 가장 작은, 개별적 단위에 대해 테스트를 진행하는 것.
ex) 함수, (React 의 경우) 컴포넌트 등... - 그렇기에 가장 양이 많고, 가장 중요시됨.
→ 어플리케이션의 가장 작은 단위들에 문제가 존재하지 않는다면, 어플리케이션 그 자체도 문제가 없을 것이라는 생각에 기반.
2. 통합 테스트
- 여러 블록 간의 조합을 테스트하는 것.
- 중요하지만, 대부분의 경우 유닛 테스트를 좀더 우선시한다고 함.
3. E2E(End-to-End) 테스트
- 완전한 시나리오/유저 플로우를 테스트하는 것.
- 중요는 하지만, 수동으로도 이러한 테스트를 진행할 수 있음 + 대체로 유닛이나 통합 단위에서 문제가 없을 경우, 전체 어플리케이션도 문제가 없을 것이라 예상할 수 있기 때문의 앞의 2개보다 중요도가 떨어지는 듯.
테스트하는 법
: 리액트의 경우, '테스트를 진행하고 결과를 도출해낼 도구' + '컴포넌트와 리액트의 앱 렌더링을 시뮬레이션 할 도구'가 필요함.
- 테스트 진행 도구 → Jest
- 시뮬레이션 도구 → React Testing Library
⇒ CRA(Create React App)으로 프로젝트를 생성할 경우, 위의 두 가지 모두 기본적으로 프로젝트에 세팅되어있음.
테스트 코드 작성
※ 일반적인 컨벤션은 테스트 코드를 테스트 대상과 가능한 한 가까이 두는 것.
테스트 작성 시 고려점(3A)
- Arrange: 테스트 데이터/조건/환경을 구성
- Act: 테스트되어야하는 로직을 실행
- Assertion: 테스트 결과를 예상 결과와 비교
※ 예제
import TestComponent from "./TestComponent";
import { render, screen } from "@testing-library/react";
// test(): 테스트를 실행하는 함수, *.test.js에서 테스트를 실행하기 위해 필요한 최소한의 함수
// 첫 번째 인자: 테스트 코드에 대한 설명
// 두 번째 인자: 익명 함수 형태의 테스트 코드
test('renders Test component as a text', () => {
// Arrange 단계 => 테스트할 컴포넌트를 가상 스크린에 렌더링
render(<TestComponent />);
// Assertion 단계 => 예상 결과(TestComponent가 Test component를 띄움)
const randomElement = screen.getByText("Test component");
// TestComponent가 document내에 존재할 것을 예상
expect(randomElement).toBeInTheDocument();
});
→ 작성한 코드는 yarn test / npm test 등으로 테스트 가능
Test VS Test Suites
- Test Suite: 서로 연관 있는 테스트 케이스들을 묶어놓은 것.
→ 어플리케이션의 규모가 커질 수록, 서로 연관이 있는(혹은 같은 컴포넌트의) 테스트들을 하나의 Test Suite로 묶는 것이 좋음.
→ Test Suite는 describe()를 통해 선언할 수 있음.
describe('Some description for test suite', () => {
// 여기에 다른 묶고자 하는 테스트들을 작성함.
test('Some test', () => {});
});
테스트에서 이벤트 트리거하기
→ @testing-library/user-event 에서 userEvent를 임포트하여 사용.
import userEvent from "@testing-library/user-event";
// 버튼을 누르면 내부의 state가 바뀌고, Changed! 텍스트가 뜨는 Greeting 컴포넌트
// 버튼이 정상적으로 작동하는 지를 확인하기 위해, 버튼의 click이벤트를 발생시킴
test('renders Changed!', () => {
render(<Greeting />);
const buttonElement = screen.getByRole('button');
// button 에 click이벤트를 트리거
userEvent.click(buttonElement);
const textElement = screen.getByText("Changed!", { exact: false });
expect(textElement).toBeInTheDocument();
});
※ screen.queryBy~
- screen.getBy~ 는 조건에 부합하는 요소를 찾지 못했을 경우 에러를 throw함.
- 반면 screen.queryBy~ 는 null을 반환하기 때문에, 특정 컴포넌트가 렌더링 되지 않는 상황을 테스트할 때 사용할 수 있음.
※ 참고
비동기 코드 테스트
- 위에서 사용한 getBy~ 의 경우, 비동기 코드 실행을 기다리지 않고 바로 조건에 부합하는 요소를 찾기 때문에 비동기 코드 테스트에 사용하기 어려움.
- findBy~ 를 사용하면 프로미스를 반환, 해당 프로미스가 성공할 때 까지 스크린을 계속해서 재평가함.
→ 즉, 특정 로직이 바로 실행되지 않고 기다려야 하는 상황에서 사용하기 적합함.
// test 함수는 async 함수 또한 받을 수 있음
test('renders posts if request succeeds', async () => {
render(<Async />);
const listItemElements = await screen.findAllByRole('listitem');
expect(listItemElements).not.toHaveLength(0);
});
- 다만, 위의 방법은 실제로 서버에 요청을 보내기 때문에 좋은 방법이 아님.
→ 다수의 테스트 코드가 존재할 경우, 서버에 너무 많은 요청을 보낼 수 있기 때문.
⇒ 따라서, mock을 사용하는 방법이 존재.
mock
- 서버에 실제 요청을 보내지 않음 + 서버와 통신하는 함수의 기능을 테스트하려는 목적이 없을 경우 사용할 수 있음.
- 일종의 더미 함수로, 원래 컴포넌트 속 함수를 대체함.
// Async.js
/*
fetch를 통해 웹에서 데이터 리스트를 받아옴
받아온 데이터를 리스트 형태로 렌더
*/
// Async.test.js
test('renders posts if request succeeds', async () => {
window.fetch = jest.fn(); // jest.fn(): window의 fetch를 mock함수로 대체함\
// 해당 mock 함수가 resolve되면 설정한 값을 반환함
window.fetch.mockResolvedValueOnce({
json: async () => [{id: 'p1', title: 'first post'}],
});
render(<Async />);
const listItemElements = await screen.findAllByRole('listitem');
expect(listItemElements).not.toHaveLength(0);
});
- 테스트 코드는 딱 한 번 작성해본 경험밖에 없어서, 기초적인 이해가 필요하겠다는 판단 하에 들었다.
- 위의 내용은 정말 간단한 내용만 포함되어있으므로, 나중에 Jest와 testing library의 공식 문서를 통해 조금 더 공부하고 실제 프로젝트에 적용하는 연습을 해보고 싶다.
'공부 > React & React Native' 카테고리의 다른 글
[React] Windowing으로 렌더링 성능 최적화하기 (0) | 2024.06.02 |
---|---|
React-Query 찍어먹기 (1) | 2024.03.04 |
Notifee 정리 (1) | 2023.05.20 |
[0814 ~ 0815] React-Native 강의 정리 (0) | 2021.08.16 |
[0812~0813] React-Native 강의 정리 (0) | 2021.08.14 |