๐Ÿ’œVitest | Vite๋ฅผ ์œ„ํ•œ ํ…Œ์ŠคํŠธ ํ”„๋ ˆ์ž„์›Œํฌ

์›์˜ยท2023๋…„ 12์›” 31์ผ
1
post-thumbnail

Vite ํ™˜๊ฒฝ์—์„œ์˜ ํ…Œ์ŠคํŠธ ํ”„๋ ˆ์ž„์›Œํฌ์ธ Vitest โœ๏ธโ—


๐Ÿ’œVitest ์„ธํŒ…

โšก1. vitest ์„ค์น˜

yarn add -D vitest

โšก2. package.json์— ์ถ”๊ฐ€

{
  "scripts": {
    "test": "vitest"
  }
}

โšก3. vitest ์„ค์ •

  • vite ํ™˜๊ฒฝ์—์„œ๋Š” vite.config.ts๋กœ ์„ค์ •
  • vite ํ™˜๊ฒฝ์ด ์•„๋‹ ์‹œ vitest.config.ts๋กœ ์„ค์ •
  • vitest์™€ ๊ด€๋ จ๋œ ์„ค์ •์„ ์ถ”๊ฐ€ํ•˜๊ฑฐ๋‚˜ ํ”Œ๋Ÿฌ๊ทธ์ธ์„ ์—ฐ๊ฒฐํ•ด์„œ ํ…Œ์ŠคํŠธ ์‹คํ–‰ ํ™˜๊ฒฝ์„ ์›ํ•˜๋Š” ๋ฐฉ์‹๋Œ€๋กœ ์ปค์Šคํ…€

โšก4. ์ด๋ฆ„ ์„ค์ •, ํด๋” ๊ตฌ์กฐ

  • src/test ํด๋” ์•ˆ์— example.test.ts ํ˜•์‹์œผ๋กœ ํ…Œ์ŠคํŠธ ํŒŒ์ผ ์ž‘์„ฑ
  • ํ˜น์€ ํ…Œ์ŠคํŠธํ•  ํ•จ์ˆ˜, ์ปดํฌ๋„ŒํŠธ์™€ ๊ฐ™์€ ํด๋”์— ์ž‘์„ฑ

๐Ÿ’œUnit Test ์‚ฌ์šฉ ์˜ˆ์‹œ

โšกํ•จ์ˆ˜ test

  • ํ•จ์ˆ˜
// sum.ts
export function sum(a: number, b: number): number {
  return a + b;
}
  • test ํŒŒ์ผ
// sum.test.ts
import { expect, test } from 'vitest';
import { sum } from '../sum';

test('๋ง์…ˆ ํ…Œ์ŠคํŠธ 1 + 2 = 3', () => {
  expect(sum(1, 2)).toBe(3);
});

์„ค๋ช…

  • test()
    • ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค๋ฅผ ์ •์˜ํ•˜๋Š” ํ•จ์ˆ˜
    • ํŠน์ • ์กฐ๊ฑด์— ๋Œ€ํ•œ ํ…Œ์ŠคํŠธ๋ฅผ ์‹คํ–‰ํ•˜๋ฉฐ, ์˜ˆ์ƒ๋œ ๊ฒฐ๊ณผ๋ฅผ ๊ฒ€์ฆ
  • expect()
    • ํ…Œ์ŠคํŠธํ•  ๊ฐ’์— ๋Œ€ํ•œ ์˜ˆ์ƒ ๊ฐ’ ์„ค์ •
    • ๋‹ค์–‘ํ•œ ๋งค์ฒ˜(matcher) ํ•จ์ˆ˜์™€ ํ•จ๊ป˜ ์‚ฌ์šฉ๋˜์–ด ๊ฐ’์„ ํŠน์ • ์กฐ๊ฑด๊ณผ ๋น„๊ตํ•˜๊ณ  ํ…Œ์ŠคํŠธ์˜ ๊ฒฐ๊ณผ ํ‰๊ฐ€
  • toBe()
    • ๊ฐ’์„ ๋น„๊ตํ•˜๊ธฐ ์œ„ํ•œ ๋งค์ฒ˜ ํ•จ์ˆ˜๋กœ, expect ํ•จ์ˆ˜์™€ ํ•จ๊ป˜ ์‚ฌ์šฉ
    • ์ •ํ™•ํ•œ ๊ฐ’๊ณผ ๋ฐ์ดํ„ฐ ํƒ€์ž…์ด ์ผ์น˜ํ•˜๋Š”์ง€๋ฅผ ํ™•์ธํ•˜์—ฌ ์˜ˆ์ƒ ๊ฐ’๊ณผ ์‹ค์ œ ๊ฐ’ ๋น„๊ต

๊ฒฐ๊ณผ

yarn test

โšก์ปดํฌ๋„ŒํŠธ test

์ปดํฌ๋„ŒํŠธ test ์œ„ํ•œ ์„ธํŒ…

  • React ๊ตฌ์„ฑ ์š”์†Œ๋ฅผ ํ…Œ์ŠคํŠธํ•˜๋ฏ€๋กœ jsdom ๊ณผ ๊ฐ™์€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ Vitest์—์„œ HTML ํ™œ์„ฑํ™” ํ•„์š”
yarn add jsdom
  • vite.config.ts ํŒŒ์ผ ์„ค์ •
import { InlineConfig, UserConfig, defineConfig } from 'vite';
import react from '@vitejs/plugin-react';

interface VitestConfigExport extends UserConfig {
  test: InlineConfig;
}

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [react()],
  test: {
    environment: 'jsdom',
  },
} as VitestConfigExport);
  • ํ•„์š”ํ•œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์„ค์น˜
- React ํ…Œ์ŠคํŒ… ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ
yarn add @testing-library/react
- ํ…Œ์ŠคํŠธ ์‹คํ–‰์„ ์œ„ํ•œ DOM ํ™˜๊ฒฝ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ
yarn add @testing-library/jest-dom
- ์œ ์ €๊ฐ€ ๋ฐœ์ƒ์‹œํ‚ค๋Š” ์ด๋ฒคํŠธ ํ…Œ์ŠคํŠธ๋ฅผ ์œ„ํ•œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ
yarn add @testing-library/user-event
  • /test/setup.ts ์— setup ํŒŒ์ผ ์ƒ์„ฑ
    • @testing-library/jest-dom ๋Š” Jest๋ฅผ ํ†ตํ•ด ํ…Œ์ŠคํŒ…์„ ํ• ๋•Œ DOM์— ๋Œ€ํ•œ ๋งค์ฒ˜(matcher)์™€ ์œ ํ‹ธ๋ฆฌํ‹ฐ ํ•จ์ˆ˜ ์ œ๊ณตํ•ด์ฃผ๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ
    • @testing-library/jest-dom/vitest ๋Š” ์œ„ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์˜ vitest ๋ฒ„์ „
    • vitest์—์„œ๋„ jest-dom์˜ ๋งค์ฒ˜๋“ค๊ณผ ์œ ํ‹ธ๋ฆฌํ‹ฐ ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉ
// setup.ts
import '@testing-library/jest-dom/vitest';
  • vite.config.ts ํŒŒ์ผ ์„ค์ •
...
test: {
    globals: true,
    environment: 'jsdom',
    setupFiles: './src/tests/setup.js',
  },
...

์ปดํฌ๋„ŒํŠธ test

  • ์ปดํฌ๋„ŒํŠธ
type InputProps = {
  label: string;
  name: string;
} & React.DetailedHTMLProps<
  React.InputHTMLAttributes<HTMLInputElement>,
  HTMLInputElement
>;

export function Input({ label, name, ...props }: InputProps) {
  return (
    <>
      <label htmlFor={name}>{label}</label>
      <input name={name} id={name} {...props} />
    </>
  );
}
  • test ํŒŒ์ผ
import { describe, expect, it } from 'vitest';
import { Input } from '../components/Input';
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';

describe('Input', async () => {
  it('Input ์ปดํฌ๋„ŒํŠธ ๋ Œ๋”๋ง', () => {
    // render() : <Input /> ๋ Œ๋”๋ง
    render(
      <Input
        name='email'
        type='email'
        placeholder='Email'
        label='Email Address'
      />
    );
    // 'Email Address' ํ…์ŠคํŠธ๋ฅผ ํฌํ•จํ•œ ์š”์†Œ๊ฐ€ ํ™”๋ฉด์— ์กด์žฌํ•˜๋Š”์ง€ ๊ฒ€์ฆ
    expect(screen.getByText('Email Address')).toBeInTheDocument();
    // 'email address'๋ผ๋Š” ๋ผ๋ฒจ์„ ๊ฐ€์ง„ ํ…์ŠคํŠธ๋ฐ•์Šค(role์ด textbox์ธ ์š”์†Œ)๊ฐ€ ํ™”๋ฉด์— ์กด์žฌํ•˜๋Š”์ง€๋ฅผ ๊ฒ€์ฆ
    // toBeInTheDocument() : ํŠน์ • ์š”์†Œ๊ฐ€ ๋ฌธ์„œ ์•ˆ์— ์กด์žฌํ•˜๋Š”์ง€๋ฅผ ํ…Œ์ŠคํŠธ
    expect(
      screen.getByRole('textbox', {
        name: /email address/i,
      })
    ).toBeInTheDocument();
  });

  // ํŠน์ • ์ž…๋ ฅ ์š”์†Œ์˜ ๊ฐ’์„ ๋ณ€๊ฒฝํ•˜๋Š”์ง€๋ฅผ ํ…Œ์ŠคํŠธ
  it('Input ๊ฐ’ ๋ณ€๊ฒฝ', async () => {
    render(
      <Input
        name='email'
        type='email'
        placeholder='Email'
        label='Email Address'
      />
    );

    const input = screen.getByRole('textbox', {
      name: /email address/i,
    });

    // userEvent.type()์„ ์‚ฌ์šฉํ•˜์—ฌ input ์š”์†Œ์— '1234' ๊ฐ’ ์ž…๋ ฅ
    await userEvent.type(input, '1234');
    // ํ•ด๋‹น input ์š”์†Œ์˜ ๊ฐ’์ด '1234'์ธ์ง€ ํ™•์ธ
    expect(input).toHaveValue('1234');
  });

์„ค๋ช…

  • describe()
    • ํ…Œ์ŠคํŠธ ์Šค์œ„ํŠธ๋ฅผ ๊ทธ๋ฃนํ™”ํ•˜๊ณ  ๊ฐ ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค ์ •์˜
  • it()
    • ์‹ค์ œ ํ…Œ์ŠคํŠธ๋ฅผ ์ •์˜ํ•˜๋Š” ํ•จ์ˆ˜

๊ฒฐ๊ณผ

profile
ํ™”์ดํŒ…~~^ใ…^/

0๊ฐœ์˜ ๋Œ“๊ธ€