Hello Kitty Eyes Shut
๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ

๐Ÿ’ป ํ”„๋กœ์ ํŠธ/๐Ÿ“Œ ํŠธ๋Ÿฌ๋ธ”์ŠˆํŒ…

[ํŠธ๋Ÿฌ๋ธ”์ŠˆํŒ…] Jest -> Vitest ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ๋ฐฉ๋ฒ•

๋ฐ˜์‘ํ˜•

 

 

 

 

 

 

๐Ÿงช ๋ณ€๊ฒฝ ์ด์œ 

Jest๋Š” Facebook์—์„œ ๋งŒ๋“  JavaScript ํ…Œ์ŠคํŒ… ํ”„๋ ˆ์ž„์›Œํฌ๋กœ, ๊ฐ•๋ ฅํ•œ ๊ธฐ๋Šฅ๊ณผ ์ƒํƒœ๊ณ„๋ฅผ ์ž๋ž‘ํ•œ๋‹ค.

๊ทธ๋Ÿฌ๋‚˜, ๋‚ด ํ”„๋กœ์ ํŠธ์˜ ๊ฐœ๋ฐœ ํ™˜๊ฒฝ์ธ Vite์™€๋Š” ๊ถํ•ฉ์ด ์ข‹์ง€ ์•Š์•„์„œ ์—ฌ๋Ÿฌ ์—๋Ÿฌ๋“ค์ด ๋ฐœ์ƒํ–ˆ๋‹ค.

 

๊ทธ๋ž˜์„œ ์• ๋ฅผ ๋จน๋˜ ์ค‘์— ..๐Ÿคฏ

Vitest๋Š” Vite์˜ ๋น ๋ฅธ ๋ฒˆ๋“ค๋ง์„ ๊ทธ๋Œ€๋กœ ํ™œ์šฉํ•ด์„œ ๋น ๋ฅธ ์‹คํ–‰์ด ๊ฐ€๋Šฅํ•˜๋ฉฐ,

Jest์™€ ์œ ์‚ฌํ•œ API๋กœ ์ง„์ž… ์žฅ๋ฒฝ์ด ๋‚ฎ์„ ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ

tsconfig, alias ๋“ฑ์˜ ์„ค์ •์ด Vite ์„ค์ •๋งŒ์œผ๋กœ ๋Œ€๋ถ€๋ถ„ ํ•ด๊ฒฐ์ด ๋œ๋‹ค๋Š” ๊ฒƒ์„ ์•Œ๊ฒŒ ๋˜์—ˆ๋‹ค.

 

๊ทธ๋ž˜์„œ Jest๋ฅผ ์ œ๊ฑฐํ•˜๊ณ  Vitest๋กœ ์ „ํ™˜ํ•˜๊ธฐ๋กœ ๊ฒฐ์ •ํ–ˆ๋‹ค.

 

 

๐Ÿ”  Jest ๐Ÿ‘‰๐Ÿป Vitest ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ๋ฐฉ๋ฒ•

1๏ธโƒฃ ๊ธฐ์กด Jest ๊ด€๋ จ ์˜์กด์„ฑ๋“ค์„ ๋ชจ๋‘ ์ œ๊ฑฐํ•˜๊ณ , Vitest ์„ค์น˜ํ•˜๊ธฐ

pnpm remove jest ts-jest jest-environment-jsdom "@types/jest"
pnpm add -D vitest @vitest/coverage-v8 jsdom

 

2๏ธโƒฃ vitest.config.ts ์ˆ˜์ •ํ•˜๊ธฐ

import { defineConfig } from 'vitest/config';
import { dirname } from 'path';
import { fileURLToPath } from 'url';
import path from 'path';
import react from '@vitejs/plugin-react';

const __filename = fileURLToPath(import.meta.url); // ํ˜„์žฌ ํŒŒ์ผ์˜ ์ „์ฒด ๊ฒฝ๋กœ
const __dirname = dirname(__filename); // ํ˜„์žฌ ํŒŒ์ผ์ด ์œ„์น˜ํ•œ ๋””๋ ‰ํ† ๋ฆฌ ๊ฒฝ๋กœ

// Vite ์„ค์ •
export default defineConfig({
  // ์‚ฌ์šฉํ•  ํ”Œ๋Ÿฌ๊ทธ์ธ ๋ชฉ๋ก
  plugins: [react()],

  // import ๊ฒฝ๋กœ ๊ด€๋ จ ์„ค์ •
  resolve: {
    alias: {
      // @์€ 'src' ํด๋”๋กœ ๋งคํ•‘๋˜๋„๋ก ์„ค์ •
      '@': path.resolve(__dirname, './src'),
    },
  },

  /* ๐Ÿ‘‡ Vitest ์ „์šฉ ์˜ต์…˜ */
  test: {
    environment: 'jsdom',
    globals: true,
    setupFiles: './src/setupTests.ts',
    coverage: {
      provider: 'v8',
      reporter: ['text', 'lcov'],
      exclude: ['dist', 'node_modules'],
    },
  },
});

 

3๏ธโƒฃ ์ปค๋ฒ„๋ฆฌ์ง€ ์„ค์ •ํ•˜๊ธฐ

pnpm add -D @vitest/coverage-v8

 

4๏ธโƒฃ TypeScrip ์ „์—ญ ํƒ€์ž… ์„ค์ •ํ•˜๊ธฐ

tsconfig.json์˜ compilerOptions ๋ธ”๋ก ์•ˆ์— types์— "vitest/globals"์„ ์ถ”๊ฐ€ํ•ด์ฃผ๋ฉด ๋œ๋‹ค.

๋‚ด ์„ค์ •์˜ ๊ฒฝ์šฐ ์•„๋ž˜์™€ ๊ฐ™๋‹ค.

{
  "files": [],
  "moduleResolution": "Node",
  "references": [
    { "path": "./tsconfig.app.json" },
    { "path": "./tsconfig.node.json" }
  ],
  "compilerOptions": {
    "target": "esnext",
    "module": "esnext",
    "moduleResolution": "node",
    "types": [
      "vite/client",
      "vitest/globals",
      "node",
      "@testing-library/jest-dom"
    ],
    "jsx": "react-jsx",
    "esModuleInterop": true,
    "baseUrl": ".",
    "paths": {
      "@/*": ["src/*"]
    }
  }
}

 

5๏ธโƒฃ API ๋ณ€๊ฒฝํ•˜๊ธฐ (jest -> vi)

/* ์ƒ˜ํ”Œ */
// Before
jest.mock('@/apis/axios');
jest.fn();

// After
vi.mock('@/apis/axios');
vi.fn();

 

6๏ธโƒฃ ์ „์—ญ CSS → CSS ๋ชจ๋“ˆํ™”

Vitest๋Š” Jest์™€ ๋‹ฌ๋ฆฌ CSS ๋ชจ๋“ˆ์„ ์ง์ ‘ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ์‹์ด ๊ถŒ์žฅ๋œ๋‹ค๊ณ  ํ•œ๋‹ค.

/* ์ƒ˜ํ”Œ */
// Before
import '@/styles/LoginForm.css';
<div className="login-form">

// After
import styles from './LoginForm.module.css';
<div className={styles.form}>

 

7๏ธโƒฃ CI ํ™˜๊ฒฝ ๋ช…๋ น ๋ณ€๊ฒฝ

Vitest๋Š” Jest์ฒ˜๋Ÿผ --ci ์˜ต์…˜์„ ์ง€์›ํ•˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— ์•„๋ž˜์™€ ๊ฐ™์ด ๋ฐ”๊ฟ”์ฃผ์—ˆ๋‹ค.

- name: Run Tests with Coverage
  run: pnpm vitest run --coverage --passWithNoTests

 

 

๐Ÿ“š ๊ฒฐ๋ก 

Jest๋กœ ํ…Œ์ŠคํŠธ๋ฅผ ์ง„ํ–‰ํ•˜๋ฉฐ ์ˆ˜๋งŽ์€ ์—๋Ÿฌ ๋•Œ๋ฌธ์— ๊ณ ์ƒํ•˜๋Š” ๊ณผ์ •์—์„œ

Vite ํ”„๋กœ์ ํŠธ๋ผ๋ฉด Vitest๊ฐ€ ์‚ฌ์‹ค์ƒ ํ‘œ์ค€์ด๋ผ๊ณ  ํ•  ์ˆ˜ ์žˆ์Œ์„ ์•Œ๊ฒŒ ๋˜์—ˆ๋‹ค.. ๐Ÿ˜“

๋ฌด์ž‘์ • ๋‚จ๋“ค์ด ๋งŽ์ด ์‚ฌ์šฉํ•˜๋Š” ๊ฑธ๋กœ ํ”„๋กœ์ ํŠธ๋ฅผ ํ•˜๋Š”๊ฒŒ ์•„๋‹ˆ๋ผ

ํ™•์‹คํžˆ ์ฐพ์•„๋ณด๊ณ  ์‹œ์ž‘ํ•ด์•ผ ๊ณ ์ƒ์„ ๋œ ํ•œ๋‹ค๋Š” ๊ฑธ ๋‹ค์‹œ๊ธˆ ๋А๋‚„ ์ˆ˜ ์žˆ์—ˆ๋‹ค ..ใ…Žใ…Ž

 

๋˜ํ•œ, Jest์—์„œ Vitest๋กœ ์˜ฎ๊ธฐ๋Š” ๊ณผ์ •์ด ๋‹จ์ˆœํ•ด๋ณด์˜€๋Š”๋ฐ,

๋ง‰์ƒ ํ•˜๋‚˜์”ฉ ๋ฐ”๊พธ๋‹ค ๋ณด๋‹ˆ๊นŒ ์—ฌ๋Ÿฌ ์—๋Ÿฌ์™€ ๋งˆ์ฃผ์ณค์–ด์„œ ์ด๋ ‡๊ฒŒ ๊ณต์œ ํ•˜๊ฒŒ ๋˜์—ˆ๋‹ค.

๋ˆ„๊ตฐ๊ฐ€์—๊ฒŒ๋Š” ๋„์›€์ด ๋˜๊ธธ ,,๐Ÿ‘๐Ÿป

๋ฐ˜์‘ํ˜•