Vite / Jest 환경 구성하기

user profile img

신현호

React

TDD 뽀개기

Post Thumbnail

목차

    서론

    구름톤 트레이닝을 듣던 도중, 리액트 강의에 TDD를 위한 내용이 들어있었습니다. 그거를 본 순간 이제는 더이상 물러날 곳이 없음을 알아버렸습니다.
    TDD의 바람이 불고있고 그게 프론트엔드라고해서 논외가 될 수는 없으니까요 흑흑..

    오늘 포스팅은 Jest, React Testing Library(RTL)에 대한 간단한 소개 및 Vite와 Jest를 통한 리액트 테스팅 환경을 구성하기를 다룰 것 입니다.
    다음 포스팅에서는 Vitest를 통한 리액트 테스팅 환경 구성을 다룰 것 같네요.

    자료를 보실 때 참고하셔야 할 점은, 블로그 포스팅은 시간이 지나면 Outdate되기때문에, 시간이 어느정도 지났다면 본 포스트만 참고하여 환경을 구성하기에는 문제가 있을 수 있음을 참고해주세요.

    Jest란?

    Jest는 메타(구 페이스북)에서 만든 테스팅 프레임워크입니다.

    Jest 등장 이전에는 Test Runner(Mocha, Jasmine, ...), Test Matcher(Chai, Expect, ...), Test Mock(Sinon, TestDouble, ...)을 모두 각기 다른 라이브러리를 사용해야했으나,
    Jest는 위에 서술한 Test Runner, Test Matcher, Test Mock을 모두 제공하고있습니다. 따라서 많이 편리하다고 할 수 있겠습니다 :)
    또 사용자가 매우 많기때문에 관련 자료도 방대한 편이기때문에 정보를 쉽게 얻을 수 있다는 장점 또한 가지고있습니다.

    테스트의 종류

    테스트에는 여러가지 종류가 있습니다. Cypress와 같은 라이브러리를 통한 E2E 테스트Jest와 같은 라이브러리/프레임워크를 통한 유닛/통합 테스트가 있습니다.

    유닛/통합 테스트

    유닛 테스트는 프로그램의 기본 단위가 되는 모듈을 테스트하는 것을 의미합니다. 즉, 최소한의 단위인 util함수, custom hook, 하나의 컴포넌트 등이 그 대상이 될 수 있습니다.
    통합 테스트는 여러개의 요소를 함께 동작시켰을 때의 테스트입니다. 모듈 간의 상호작용이 정상적으로 수행되는지 검증하는게 주된 목표입니다.

    E2E 테스트

    E2E(End To End) 테스트란, 애플리케이션의 흐름을 처음부터 끝까지 테스트하는 것을 의미합니다.
    유닛 테스트와 통합 테스트가 모듈의 무결성을 증명한다면, E2E테스트는 애플리케이션 동작을 테스트하며 궁극적으로는 애플리케이션의 무결성을 증명한다고 할 수 있습니다.

    React에서의 테스트

    RTL

    React에서 추천되는 테스트 도구는 바로 React Testing Library 입니다. React Testing Library(RTL)를 사용하면 쉽게 React의 테스트를 작성할 수 있습니다.

    RTL에 대해서 조금 더 이야기를 해보자면, 원래 RTL 이전에는 Airbnb의 Enzyme이라는 라이브러리가 존재했습니다.
    RTL과 Enzyme의 차이점은, Enzyme은 테스트를 렌더링 할 때 가상 DOM에 렌더링을 하고 RTL은 실제 DOM 노드에 렌더링을 한다는 점 입니다.
    그렇기때문에 RTL은 사용자가 웹 브라우저에서 애플리케이션을 실행하는 실제 환경과 더 유사한 환경에서 테스트를 진행할 수 있습니다.

    저는 이 점 때문에 RTL이 Enzyme보다 현재 더 인기가 있지 않은가 생각하고 있습니다. 아래 글 또한 한번 읽어보시는 것을 추천드립니다 :)

    Vite와 Jest를 통한 React 테스트 환경 구축하기

    그럼, 현재 react를 시작할 때 가장 많이 사용되는 Vite와 Jest를 통한 React 테스트 환경을 구축해보도록 하겠습니다.
    전반적인 설정은 하단의 링크를 참고하여 이루어지나, 2024년 현재 바뀐 부분을 반영하였으며 본 설정에서는 Eslint 설정도 함께 진행합니다

    설명은 vite로 프로젝트를 생성했다고 가정하고 진행됩니다.

    1. 라이브러리 설치

    필요한 라이브러리를 설치합니다.

    bash

    npm install -D jest @types/jest ts-node ts-jest @testing-library/react identity-obj-proxy jest-environment-jsdom @testing-library/jest-dom jest-svg-transformer
    npm install -D eslint-plugin-testing-library eslint-plugin-jest-dom
    

    2. package.json 수정

    package.json에 다음 내용을 추가합니다.

    json

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

    3. jest 설정파일 생성

    루트폴더에 jest.setup.ts 파일을 생성해줍니다.

    ts

    import '@testing-library/jest-dom'
    

    루트폴더에 jest.config.js 파일을 생성해줍니다.

    js

    export default {
      testEnvironment: 'jsdom',
      transform: {
        '^.+\\.tsx?$': 'ts-jest',
      },
      moduleNameMapper: {
        '^.+\\.svg$': 'jest-svg-transformer',
        '\\.(css|less|sass|scss)$': 'identity-obj-proxy',
      },
      setupFilesAfterEnv: ['<rootDir>/jest.setup.ts'],
    }
    

    4. tsconfig 세팅

    tsconfig.json에 다음과 같은 내용을 추가합니다.

    json

    {
      "compilerOptions": {
        ...,
        "esModuleInterop": true
      }
    }
    

    5. eslint 세팅하기

    .eslintrc.cjs를 다음과 같이 수정합니다.

    module.exports = {
      root: true,
      env: { browser: true, es2020: true },
      extends: [
        'eslint:recommended',
        'plugin:@typescript-eslint/recommended',
        'plugin:react-hooks/recommended',
        'plugin:testing-library/react',
        'plugin:jest-dom/recommended',
      ],
      ignorePatterns: ['dist', '.eslintrc.cjs'],
      parser: '@typescript-eslint/parser',
      plugins: ['react-refresh', 'testing-library', 'jest-dom'],
      rules: {
        'react-refresh/only-export-components': [
          'warn',
          { allowConstantExport: true },
        ],
      },
    }
    

    6. 테스트 코드 추가하기

    src 디렉토리에 __tests__ 디렉토리를 만들고 App.test.tsx를 만들어줍니다.

    tsx

    // Imports
    import { render, screen, fireEvent } from '@testing-library/react'
    
    // To Test
    import App from '../App'
    
    // Tests
    test('Renders main page correctly', async () => {
      // Setup
      render(<App />)
      const buttonCount = await screen.findByRole('button')
      const codeCount = screen.queryByText(/count is/)
    
      // Pre Expecations
      expect(buttonCount.innerHTML).toBe('count is 0')
      // Instead of:
      // expect(codeCount).toBeNull();
      expect(codeCount).not.toBeInTheDocument()
    
      // Init
      fireEvent.click(buttonCount)
      fireEvent.click(buttonCount)
    
      // Post Expectations
      expect(buttonCount.innerHTML).toBe('count is 2')
      expect(screen.getByText(/The count is now:/)).toBeInTheDocument()
    })
    

    이 부분에서 toBeInTheDocument 파트 ts 에러가 발생할 수 있는데, 이 때는 tsconfig에 다음 속성을 추가해주시면 됩니다.

    json

    {
      "compilerOptions": {
        ...,
        "types": ["@testing-library/jest-dom"]
      }
    }
    

    7. App.tsx 내용 변경하기

    테스트를 위해 App.tsx의 내용을 변경해줍니다.

    tsx

    import { useState } from 'react'
    import reactLogo from './assets/react.svg'
    import viteLogo from '/vite.svg'
    import './App.css'
    
    function App() {
      const [count, setCount] = useState(0)
    
      return (
        <>
          <div>
            <a href="https://vitejs.dev" target="_blank">
              <img src={viteLogo} className="logo" alt="Vite logo" />
            </a>
            <a href="https://react.dev" target="_blank">
              <img src={reactLogo} className="logo react" alt="React logo" />
            </a>
          </div>
          <h1>Vite + React1</h1>
          <div className="card">
            <button onClick={() => setCount((count) => count + 1)}>
              count is {count}
            </button>
            {count > 0 ? (
              <p>
                <code>The count is now: {count}</code>
              </p>
            ) : null}
            <p>
              Edit <code>src/App.tsx</code> and save to test HMR
            </p>
          </div>
          <p className="read-the-docs">
            Click on the Vite and React logos to learn more
          </p>
        </>
      )
    }
    
    export default App
    

    결과

    TEST

    끝으로

    해당 실습 자료를 깃허브에 남겨놨습니다. 자세한 코드는 해당 레포지토리를 참조해주시면 되겠습니다:)

    참고

    Profile Image

    신현호

    Frontend Developer

    프론트엔드 개발자를 꿈꾸고 있는 대학생입니다. 끊임없이 배우고 성장하는 개발자가 되기 위해 노력하고 있습니다.

    TDD 뽀개기

    총 1개의 포스트가 존재합니다.