코드 품질을 보장하고, 기능이 의도한 대로 동작하는지 확인하기 위해 테스트 코드를 작성한다. 특히 기능 추가나 리팩토링을 할 때 테스트 코드가 있으면 기존 기능이 올바르게 작동하는지 쉽게 확인할 수 있고, 심리적인 안정감을 주는 장점도 있다. 프론트엔드 테스트 종류는 크게 단위 테스트, 통합 테스트, 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>

기본 설정

  1. 패키지 설치

    pnpm i -D vitest @testing-library/react @testing-library/jest-dom jsdom
    
  2. 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'],
      },
    });
    
  3. 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 에 렌더링된 요소 제거
    });
    
  4. package.json 스크립트 추가 (설정시 pnpm test 명령어로 테스트 실행 가능)

    {
      "scripts": {
        // ...
        "test": "vitest"
      }
    }
    
  5. 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 에러가 사라진다.

    Untitled

테스트 코드 작성

<aside> <img src="/icons/search_gray.svg" alt="/icons/search_gray.svg" width="40px" /> getByRole 함수의 name 옵션은 접근성 이름을 가리키며, 아래 방법으로 설정될 수 있다.

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");
  });
});

테스트 성공 화면

테스트 성공 화면