0%
実装パターン#テスト#Jest#Vitest

テスト環境の構築|Jest/Vitestでテストを書き始める方法

Next.jsプロジェクトでのテスト環境構築方法。Jest、Vitest、Testing Libraryの設定を解説。

||11分で読める

テスト環境の構築

テストがあると安心して開発できる。

テストの種類

1. ユニットテスト: 関数単位
2. 統合テスト: API、DB連携
3. E2Eテスト: ブラウザ操作

Vitestセットアップ(推奨)

インストール

npm install -D vitest @vitejs/plugin-react jsdom @testing-library/react @testing-library/jest-dom

設定ファイル

// vitest.config.ts
import { defineConfig } from 'vitest/config'
import react from '@vitejs/plugin-react'
import path from 'path'

export default defineConfig({
  plugins: [react()],
  test: {
    environment: 'jsdom',
    setupFiles: ['./vitest.setup.ts'],
    globals: true,
  },
  resolve: {
    alias: {
      '@': path.resolve(__dirname, './'),
    },
  },
})

セットアップファイル

// vitest.setup.ts
import '@testing-library/jest-dom'

package.json

{
  "scripts": {
    "test": "vitest",
    "test:run": "vitest run",
    "test:coverage": "vitest run --coverage"
  }
}

Jestセットアップ

インストール

npm install -D jest jest-environment-jsdom @testing-library/react @testing-library/jest-dom

設定ファイル

// jest.config.js
const nextJest = require('next/jest')

const createJestConfig = nextJest({
  dir: './',
})

const customJestConfig = {
  setupFilesAfterEnv: ['<rootDir>/jest.setup.js'],
  testEnvironment: 'jest-environment-jsdom',
  moduleNameMapper: {
    '^@/(.*)$': '<rootDir>/$1',
  },
}

module.exports = createJestConfig(customJestConfig)

テストの書き方

関数のテスト

// lib/utils.test.ts
import { describe, it, expect } from 'vitest'
import { formatPrice } from './utils'

describe('formatPrice', () => {
  it('数値を円表記に変換する', () => {
    expect(formatPrice(1000)).toBe('¥1,000')
  })

  it('0の場合は¥0を返す', () => {
    expect(formatPrice(0)).toBe('¥0')
  })
})

コンポーネントのテスト

// components/Button.test.tsx
import { describe, it, expect, vi } from 'vitest'
import { render, screen, fireEvent } from '@testing-library/react'
import { Button } from './Button'

describe('Button', () => {
  it('テキストを表示する', () => {
    render(<Button>Click me</Button>)
    expect(screen.getByText('Click me')).toBeInTheDocument()
  })

  it('クリックでonClickが呼ばれる', () => {
    const handleClick = vi.fn()
    render(<Button onClick={handleClick}>Click me</Button>)

    fireEvent.click(screen.getByText('Click me'))
    expect(handleClick).toHaveBeenCalledTimes(1)
  })

  it('disabledの場合はクリックできない', () => {
    const handleClick = vi.fn()
    render(<Button onClick={handleClick} disabled>Click me</Button>)

    fireEvent.click(screen.getByText('Click me'))
    expect(handleClick).not.toHaveBeenCalled()
  })
})

APIのテスト

// app/api/users/route.test.ts
import { describe, it, expect, vi } from 'vitest'
import { POST } from './route'

describe('POST /api/users', () => {
  it('ユーザーを作成できる', async () => {
    const request = new Request('http://localhost/api/users', {
      method: 'POST',
      body: JSON.stringify({ email: 'test@example.com' }),
    })

    const response = await POST(request)
    const data = await response.json()

    expect(response.status).toBe(200)
    expect(data.email).toBe('test@example.com')
  })
})

モック

関数のモック

import { vi } from 'vitest'

const mockFetch = vi.fn()
global.fetch = mockFetch

mockFetch.mockResolvedValue({
  ok: true,
  json: () => Promise.resolve({ data: 'test' }),
})

モジュールのモック

vi.mock('@/lib/db', () => ({
  prisma: {
    user: {
      findUnique: vi.fn(),
      create: vi.fn(),
    },
  },
}))

E2Eテスト(Playwright)

インストール

npm init playwright@latest

テスト例

// e2e/login.spec.ts
import { test, expect } from '@playwright/test'

test('ログインできる', async ({ page }) => {
  await page.goto('/login')

  await page.fill('input[name="email"]', 'test@example.com')
  await page.fill('input[name="password"]', 'password123')
  await page.click('button[type="submit"]')

  await expect(page).toHaveURL('/dashboard')
})

CIでテスト実行

# .github/workflows/test.yml
name: Test

on: [push, pull_request]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 20
      - run: npm ci
      - run: npm test

次のステップ

シェア:

参考文献・引用元

実装パターンの他の記事

他のカテゴリも見る