実装パターン#テスト#Jest#Vitest
テスト環境の構築|Jest/Vitestでテストを書き始める方法
Next.jsプロジェクトでのテスト環境構築方法。Jest、Vitest、Testing Libraryの設定を解説。
テスト環境の構築
テストがあると安心して開発できる。
テストの種類
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
次のステップ
参考文献・引用元
- [1]
- [2]
- [3]Playwright Documentation- Microsoft