단위 테스트 도입기

김가희·2024년 5월 11일
0

단위 테스트 정의

단위 테스트(Unit Testing)는 가장 작은 코드 단위(보통 함수나 메소드)가 의도한 대로 정확하게 동작하는지 검증하는 테스트 방법이다. 개별 코드 구성 요소가 예상대로 동작함을 확인함으로써, 소프트웨어의 안정성과 품질을 개선하는 데 중요한 역할을 한다.



단위 테스트 도입 목적

내가 단위 테스트를 도입하게 된 목적은 코드 변경 후의 수동 검사로 인한 문제점을 해결하기 위함이었다.

지금까지는 코드 변경 후에 웹사이트에서 수동으로 하나하나 검사하는 방식으로 테스트를 하고 있었다. 하지만 이는 시간이 많이 소요되고, 의도치 않은 버그를 놓치기 쉽다는 단점이 있다. 실제로 다른 기능을 개발하다가 누락된 부분을 발견하는 경우가 잦아 개발에 어려움을 겪고 있었다.

  1. 시간 및 비용 절감
  2. 버그의 조기 발견 및 수정
  3. 개발 프로세스의 효율성

따라서 위의 세 가지를 목적으로 단위 테스트를 도입하고자 한다.



단위 테스트 도입을 위한 준비

단위 테스트 도구 선정

React 개발 환경에서 사용할 수 있는 주요 테스트 도구들에는 여러 가지가 있다.

도구장점단점
Jest- Facebook에 의해 개발되어 React와 잘 통합되며, 설정이 간단하다.
- 스냅샷 테스팅, 테스트 커버리지 분석 등 다양한 기능을 내장하고 있고, 복잡한 설정 없이도 많은 기능을 사용할 수 있다.
- 동시에 실행되는 테스트 덕분에 테스트 속도가 빠르다.
- 때때로 대규모 프로젝트에서 Jest의 자동 모킹 시스템이 예상치 못한 문제를 일으킬 수 있다.
Mocha유연성이 뛰어나고 확장 가능한 테스트 프레임워크로, 다양한 보조 라이브러리와 플러그인을 통해 필요한 테스트 환경을 구성할 수 있다.- Jest에 비해 초기 설정이 복잡할 수 있다.
- 기본적으로 제공하는 기능이 적어 추가 패키지를 설치하고 설정해야 할 수도 있다.
Enzyme- AirBnB에서 개발한 테스팅 유틸리티로, DOM 노드의 조작 및 트래버싱이 용이하다.
- 특히 구조적인 테스팅에 유리하며, 컴포넌트의 라이프사이클을 테스트할 수 있는 API를 제공한.
- React 16 이후 버전에서는 일부 기능 호환성 문제가 있으며, React의 후속 업데이트와의 통합이 지연될 수 있다.
Cypress브라우저에서 직접 테스트를 실행하여 실제 사용자와 가장 유사한 환경에서 테스팅을 할 수 있는 E2E 테스팅 도구다.단위 테스트보다는 통합 및 E2E 테스트에 초점을 맞추고 있어, 컴포넌트 레벨의 단위 테스트에는 다소 과할 수 있다.

나는 여기서 Jest를 선택했다. React와의 통합성이 좋고, 초기 설정이 간단하며 다양한 유용한 기능을 제공하기 때문이다.
테스트에 익숙하지 않은 입문자도 쉽게 시작할 수 있을 것이라 판단했다.


Jest와 React Testing Library 조합

두 라이브러리를 꼭 함께 사용해야 하는 건 아니지만, JestReact Testing Library는 서로 보완하여 사용될 때 가장 효과적이라고 한다.
React Testing Library는 사용자 중심의 테스트 작성을 위해 설계되었다. 컴포넌트의 내부 구현이 아닌, 사용자가 보는 화면과의 상호작용을 중심으로 테스트한다.

이 두 도구의 조합 활용 시, 아래처럼 역할을 분담한다.

Jest의 역할: 테스트 케이스를 발견, 실행, 그리고 결과를 리포팅하는 전체적인 테스트 프로세스를 관리
React Testing Library의 역할: UI 컴포넌트의 렌더링 및 사용자 상호작용을 테스트하는 데 집중한다.

결과적으로 사용자 친화적인 테스트를 구성할 수 있게 하여, 높은 품질의 애플리케이션을 보다 효과적으로 개발할 수 있도록 돕는다.


단위 테스트 환경 설정

1. 필요한 도구 설치하기

npm install --save-dev jest @types/jest ts-jest jest-environment-jsdom
npm install --save-dev @testing-library/react @testing-library/jest-dom
npm install --save-dev @babel/preset-react @babel/preset-typescript @babel/preset-env
  • jest

  • ts-jest: TypeScript와 함께 Jest를 사용할 수 있도록 하는 라이브러리

  • jest-environment-jsdom: 원래 Jest에서 기본적으로 제공되었지만 Jest 28 버전부터는 별도로 설치 필요

  • @testing-library/react

  • @testing-library/jest-dom: Jest에서 DOM 노드를 더 쉽게 검증할 수 있도록 하는 추가적인 Jest 매처들을 제공

  • @babel/preset-react: Babel을 사용하여 React JSX 구문을 JavaScript 코드로 변환해주는 Babel 프리셋

  • @babel/preset-typescript: TypeScript 코드를 JavaScript로 변환하기 위한 Babel 프리셋

  • @babel/preset-env: JavaScript 코드를 특정 환경에 맞게 변환해주는 Babel 프리셋으로, ES6+ 코드를 하위 호환 가능한 ES5 코드로 변환하는 등의 작업을 수행


2. package.json 설정

"scripts": {
	...
  	"test": "jest"
}

3. jest.config.js 설정

jest.config.js 파일을 프로젝트 루트에 생성하고 아래와 같이 설정한다. 이 설정은 Jest가 TypeScript 코드를 이해하고, React 컴포넌트를 적절히 처리할 수 있도록 한다.

module.exports = {
  preset: 'ts-jest',
  testEnvironment: 'jsdom',
  setupFilesAfterEnv: ['@testing-library/jest-dom/extend-expect'],
  moduleNameMapper: {
    // CSS Modules 및 이미지 같은 자산을 모킹 처리
    '\\.(css|less|sass|scss)$': 'identity-obj-proxy',
    '\\.(gif|ttf|eot|svg|png)$': '<rootDir>/__mocks__/fileMock.js'
  },
  testMatch: [
    '<rootDir>/**/*.test.(js|jsx|ts|tsx)',
    '<rootDir>/(tests/unit/**/*.spec.(js|jsx|ts|tsx)|**/__tests__/*.(js|jsx|ts|tsx))',
  ],
  transform: {
    '^.+\\.(js|jsx|ts|tsx)$': 'ts-jest',
  },
  transformIgnorePatterns: ['<rootDir>/node_modules/']
};

4. tsconfig.json 설정

tsconfig.json에서 Jest 관련 타입 오류를 방지하기 위해 다음 설정을 추가한다.

{
  "compilerOptions": {
    "types": ["jest", "node"]
  }
}

5. babel.config.json 설정

프로젝트 루트에 babel.config.json 파일을 생성하고 다음과 같이 설정한다.

{
  "presets": [
    [
      "@babel/preset-env",
      {
        "targets": {
          "node": "current"
        }
      }
    ],
    "@babel/preset-react",
    "@babel/preset-typescript"
  ]
}

6. 테스트 스크립트 작성

컴포넌트의 테스트를 작성한다. 예를 들어, 간단한 버튼 컴포넌트에 대한 테스트는 다음과 같이 작성할 수 있다.

import React from 'react';
import { render, screen } from '@testing-library/react';
import Button from './Button';

describe('Button', () => {
  test('renders with text', () => {
    render(<Button>Click Me!</Button>);
    const buttonElement = screen.getByText(/click me!/i);
    expect(buttonElement).toBeInTheDocument();
  });
});


테스트 작성 범위 정하기

실질적으로 모든 기능을 테스트 하는 것은 비효율적이라고 한다. 요구 사항이 명시적이고 변할 가능성이 없다면 테스트 기반 개발(TDD)을 해도 좋지만, 현업에서는 요구 사항이 자주 바뀔 수 있기 때문이다. 요구 사항이 자주 바뀌면 그만큼 테스트 코드를 자주 수정해야 하고 유지보수 코스트가 계속 증가하게 된다.

모든 코드를 테스트하는 것이 이상적일 수 있지만, 실용적인 관점에서 선택과 집중이 필요하다고 한다.

그래서 추천하는 테스트 작성 범위는 아래와 같다.

  • 주요 기능의 핵심 비즈니스 로직
  • 반복적으로 사용되는 재사용 가능한 유틸리티 함수 및 컴포넌트
  • 데이터 처리, API 통신 등 외부 의존성이 큰 부분의 테스트
  • 유틸리티 함수


실제로 적용해 보기

테스트 작성 범위 정하기

내가 테스트 해 볼 수 기능은 다음과 같다.

  • 핵심 비즈니스 로직
    1. 이벤트 등록
    2. 이벤트 관리(수정 및 삭제)
    3. 이벤트 검색 및 필터링
    4. 이벤트 상세 정보
    5. 이벤트 참여 신청
    6. 장바구니와 결제
    7. 주문 관리
  • 재사용 컴포넌트
    1. 이벤트 카드 컴포넌트
    2. 장바구니 아이템 컴포넌트
  • 외부 의존성
    1. 결제 SDK
    2. 소셜 로그인 API
  • 유틸리티 함수

테스트 코드 작성해 보기

일단 유틸리티 함수 테스트 코드부터 작성해 보았다.
함수를 테스트 하는 것은 큰 어려움이 없었는데, 비즈니스 로직이나 외부 의존성 부분을 테스트 하는 것이 어려울 것 같다. 좀 무섭지만 힘내서 도전~!!



참고: https://velog.io/@a770418/Vite-TypeScript-환경에서-babel을-이용한-jest-적
참고: https://velog.io/@sjmh0507/React-TS에서-Jest-세팅하기

0개의 댓글