코드 품질을 보장하고, 기능이 의도한 대로 동작하는지 확인하기 위해 테스트 코드를 작성한다. 특히 기능 추가나 리팩토링을 할 때 테스트 코드가 있으면 기존 기능이 올바르게 작동하는지 쉽게 확인할 수 있고, 심리적인 안정감을 주는 장점도 있다. 프론트엔드 테스트 종류는 크게 단위 테스트, 통합 테스트, E2E 테스트, 정적 테스트로 나뉜다.
테스트 종류 | 설명 | 예시 | 주요 도구 |
---|---|---|---|
단위 테스트 (Unit Test) | 개별 함수, 컴포넌트, 모듈의 동작 검증. 가장 작은 단위의 테스트로 격리된 환경에서 진행. | 버튼 클릭 시 특정 함수 호출 여부 | Jest, Vitest, Mocha, Jasmine 등 |
통합 테스트 (Integration Test) | 여러 모듈이 함께 잘 작동하는지 확인. 컴포넌트 간 상호작용, API 연동 등 테스트. | 상품 구매 시 잔액 업데이트, 재고 변경 | Jest, Vitest, React Testing Library(UI Test) 등 |
E2E 테스트 (End To End Test) | 사용자 관점에서 전체 애플리케이션의 흐름 테스트. 실제 사용 환경과 유사한 조건에서 진행. | 로그인부터 상품 구매까지의 전체 과정 | Cypress, Selenium, Playwright, Puppeteer 등 |
정적 테스트 (Static Test) | 코드 실행 없이 소스 코드 자체 분석. 타입 체크, 코드 스타일, 잠재적 오류 등 검사. | TypeScript 타입 검사, ESLint 코드 스타일 검증 | ESLint, Prettier, TypeScript 등 |
아래에서 Vitest, React Testing Library를 이용해 테스트 환경을 구축하는 방법에 대해 알아보자(Vite, React, TypeScript 기반).
Vitest는 Vite 기반 테스트 프레임워크로 Vite 설정과 플러그인을 그대로 사용할 수 있고, 대부분의 Jest API와 호환된다. React Testing Library는 버튼 클릭, 텍스트 입력 등 컴포넌트를 실제 사용자처럼 상호작용하며 테스트하도록 설계된 도구다.
<aside> <img src="/icons/search_gray.svg" alt="/icons/search_gray.svg" width="40px" /> 패키지 매니저는 pnpm을 사용했다.
</aside>
패키지 설치
pnpm i -D vitest @testing-library/react @testing-library/jest-dom jsdom
vite.config.ts 파일 수정
/// <reference types="vitest" />
/// <reference types="vite/client" />
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
export default defineConfig({
plugins: [react()],
test: {
// describe, it 등 전역 테스트 함수를 import 없이 사용할 수 있도록 설정
globals: true,
// jsdom 환경을 사용하여 브라우저 환경 시뮬레이션
environment: 'jsdom',
// 테스트 실행 전 특정 파일이나 스크립트를 실행하도록 지정
setupFiles: ['./src/setup-tests.ts'],
},
});
src 폴더에 setup-tests.ts 파일 생성
import * as matchers from '@testing-library/jest-dom/matchers';
import { expect } from 'vitest';
import { cleanup } from '@testing-library/react';
/**
* jest-dom 에서 제공하는 매처를 Vitest expect 함수에서 사용할 수 있도록 확장
* 매처는 toBeInTheDocument 등과 같은 DOM 상태를 검증하는 메서드
* */
expect.extend(matchers);
afterEach(() => {
cleanup(); // 각 테스트 종료 후 DOM 에 렌더링된 요소 제거
});
package.json 스크립트 추가 (설정시 pnpm test
명령어로 테스트 실행 가능)
{
"scripts": {
// ...
"test": "vitest"
}
}
tsconfig.app.json 수정 (Vitest, Testing Library 타입 정의 추가)
{
"compilerOptions": {
"composite": true,
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
"target": "ES2020",
"useDefineForClassFields": true,
"lib": ["ES2020", "DOM", "DOM.Iterable"],
"module": "ESNext",
"skipLibCheck": true,
"types": ["vitest/globals", "@testing-library/jest-dom"], // 추가
// ...
},
"include": ["src"]
}
위처럼 타입 정의를 추가해두면 TS2582 에러가 사라진다.
<aside>
<img src="/icons/search_gray.svg" alt="/icons/search_gray.svg" width="40px" /> getByRole 함수의 name
옵션은 접근성 이름을 가리키며, 아래 방법으로 설정될 수 있다.
aria-label
속성 : 요소에 명시적으로 접근성 이름 지정aria-labelledby
속성 : 다른 요소의 텍스트 콘텐츠를 접근성 이름으로 사용alt
속성 : 이미지 요소의 대체 텍스트를 접근성 이름으로 사용
</aside>App.test.tsx 파일 생성 → 아래 테스트 코드를 추가한 후 터미널에 pnpm test
를 입력해보자. 1 passed 표시가 나오면 테스트 환경설정이 성공적으로 완료된 것이다.
import { fireEvent, render, screen } from "@testing-library/react";
import App from "./App.tsx";
describe("App component", () => {
test("increments count when button is clicked", () => {
render(<App />);
const button = screen.getByRole("button", { name: /count is \\d/i });
fireEvent.click(button);
expect(button).toHaveTextContent("count is 1");
});
});
테스트 성공 화면