Jest・Vitest・Playwrightの使い分け|2026年テスト戦略完全ガイド
Jest・Vitest・Playwrightの比較・選定ガイド。単体テスト・E2Eテストの実装パターンとテストアーキテクチャ設計を解説。2026年最新版。
Jest・Vitest・Playwrightの使い分け完全ガイド2026|テスト戦略の最適選定
2026年時点で、Web開発のテスト環境は劇的に進化しました。Jest、Vitest、Playwrightは、それぞれ異なるテスト層で活躍するツールです。本記事では、これら3つのテストフレームワークの最新バージョン(Jest 30.x、Vitest 2.x、Playwright 1.50.x)を前提に、適切なテスト戦略の構築方法を解説します。
単なるツール比較ではなく、実案件で必要とされるテストアーキテクチャ設計と効率的な運用方法にフォーカスし、コード例を交えて実装戦略を提示します。
2026年のテストツール環境整理|3つのレイヤーの役割分担
graph TD
A["エンドユーザー操作"]
B["Playwright E2Eテスト"]
C["統合テスト"]
D["Vitest/Jest"]
E["コンポーネント・関数"]
F["Jest単体テスト"]
G["プリミティブ関数"]
A --> B
B --> C
C --> D
D --> E
E --> F
F --> G
style B fill:#ff6b6b
style D fill:#4ecdc4
style F fill:#95e1d3
2026年のWeb開発では、テスト戦略がピラミッド構造からバランス型アーキテクチャへシフトしています。以下の3つのレイヤーを理解することが、効率的なテスト運用の第一歩です。
| テストツール | テスト層 | 実行速度 | カバレッジ範囲 | 導入難度 | 推奨用途 |
|---|---|---|---|---|---|
| Jest | 単体テスト | 高速(数ms) | 関数・モジュール単位 | 低 | ビジネスロジック、ユーティリティ |
| Vitest | 単体・統合テスト | 超高速(<10ms) | 関数・コンポーネント単位 | 低 | React/Vue コンポーネント、モダンプロジェクト |
| Playwright | E2Eテスト | 低速(秒単位) | アプリケーション全体 | 高 | ユーザーフロー、本番検証 |
Jest vs Vitest|2026年の最新動向
Jest 30.xは安定性を重視し、Vitest 2.xはパフォーマンスと開発体験を極めています。2026年時点で、以下の判断基準で選択するプロジェクトが増加中です:
Jestを選ぶべき場合:
- Next.js App Router + SSR/SSGを多用するプロジェクト
- 大規模エンタープライズプロジェクト(テストの保守性重視)
- TypeScript型チェック統合が必須な場合
- Jest プラグインエコシステムの充実が必要
Vitestを選ぶべき場合:
- Vite/Next.js 15 (App Router) ネイティブプロジェクト
- フロントエンド開発でTDD開発を実践(高速フィードバック重視)
- Vitest UI(ブラウザベース対話型テストランナー)を活用したい
- ESM専用プロジェクト
実装例:Jest 30.xの最新セットアップ
// jest.config.mjs (ESM対応)
export default {
preset: 'ts-jest',
testEnvironment: 'node',
roots: ['<rootDir>/src'],
testMatch: ['**/__tests__/**/*.test.ts', '**/?(*.)+(spec|test).ts'],
moduleNameMapper: {
'^@/(.*)$': '<rootDir>/src/$1',
},
collectCoverageFrom: [
'src/**/*.ts',
'!src/**/*.d.ts',
'!src/index.ts',
],
coverageThreshold: {
global: {
branches: 80,
functions: 80,
lines: 80,
statements: 80,
},
},
testTimeout: 10000,
globals: {
'ts-jest': {
tsconfig: {
jsx: 'react-jsx',
},
},
},
};
実装例:Vitest 2.xの高速テスト実行
// vitest.config.ts
import { defineConfig } from 'vitest/config';
import react from '@vitejs/plugin-react';
import path from 'path';
export default defineConfig({
plugins: [react()],
test: {
globals: true,
environment: 'jsdom',
setupFiles: ['./vitest.setup.ts'],
threads: true,
maxThreads: 4,
minThreads: 1,
coverage: {
provider: 'v8',
reporter: ['text', 'json', 'html'],
exclude: [
'node_modules/',
'dist/',
'build/',
],
},
ui: true,
},
resolve: {
alias: {
'@': path.resolve(__dirname, './src'),
},
},
});
Playwrightの2026年型E2Eテスト戦略
Playwright 1.50.xは、AI支援によるテスト生成、マルチブラウザ対応、デバッグ機能の大幅強化が実現されました。
Playwrightの主な進化(2026年版)
pie title Playwright E2Eテストカバレッジの実装比率(2026年)
"UI インタラクション" : 45
"ビジュアルリグレッション" : 25
"API モック統合" : 20
"パフォーマンス検証" : 10
Playwright設定の実装例
// playwright.config.ts
import { defineConfig, devices } from '@playwright/test';
export default defineConfig({
testDir: './e2e',
fullyParallel: true,
forbidOnly: !!process.env.CI,
retries: process.env.CI ? 2 : 0,
workers: process.env.CI ? 1 : undefined,
reporter: [
['html'],
['junit', { outputFile: 'test-results.xml' }],
['json', { outputFile: 'test-results.json' }],
],
globalSetup: require.resolve('./global-setup.ts'),
globalTeardown: require.resolve('./global-teardown.ts'),
use: {
baseURL: 'http://localhost:3000',
trace: 'on-first-retry',
screenshot: 'only-on-failure',
video: 'retain-on-failure',
},
projects: [
{
name: 'chromium',
use: { ...devices['Desktop Chrome'] },
},
{
name: 'firefox',
use: { ...devices['Desktop Firefox'] },
},
{
name: 'webkit',
use: { ...devices['Desktop Safari'] },
},
{
name: 'Mobile Chrome',
use: { ...devices['Pixel 5'] },
},
],
webServer: {
command: 'npm run dev',
url: 'http://localhost:3000',
reuseExistingServer: !process.env.CI,
},
});
Playwrightテストの実装例:複雑なユーザーフロー検証
// e2e/checkout-flow.spec.ts
import { test, expect, Page } from '@playwright/test';
test.describe('Eコマース決済フロー', () => {
test('クレジットカード決済の完全フロー', async ({ page, context }) => {
// ステップ1:ログイン
await page.goto('/login');
await page.fill('input[name="email"]', 'user@example.com');
await page.fill('input[name="password"]', 'password123');
await page.click('button[type="submit"]');
await expect(page).toHaveURL('/dashboard');
// ステップ2:商品検索・カート追加
await page.goto('/products');
await page.click('button[data-testid="filter-electronics"]');
await page.waitForLoadState('networkidle');
const productCard = page.locator('[data-testid="product-card"]').first();
await expect(productCard).toBeVisible();
await productCard.click();
// ステップ3:商品詳細確認
await page.click('button[aria-label="増加"]');
await page.click('button:has-text("カートに追加")');
// ステップ4:チェックアウト
await page.goto('/checkout');
await context.addInitScript(() => {
(window as any).__mockAPI__ = {
paymentGateway: { success: true },
};
});
// ステップ5:配送情報入力
await page.fill('input[name="address"]', '123 Main St');
await page.selectOption('select[name="shipping"]', 'express');
// ステップ6:決済実行
await page.click('button:has-text("支払う")');
// 確認
await expect(page).toHaveURL('/order-confirmation');
await expect(page.locator('text=ご注文ありがとうございます')).toBeVisible();
});
test('決済ページのビジュアルリグレッション', async ({ page }) => {
await page.goto('/checkout');
await page.waitForLoadState('networkidle');
await expect(page).toHaveScreenshot('checkout-page.png', {
mask: [
page.locator('[data-testid="promo-banner"]'),
],
});
});
});
テスト戦略の統合実装|3層テストの協調
実践的なプロジェクトでは、Jest/VistestとPlaywrightを統合してテスト戦略を構築します。
実装例:統合テストスイートの構成
// src/services/__tests__/auth.service.test.ts
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
import { AuthService } from '../auth.service';
describe('AuthService', () => {
let authService: AuthService;
beforeEach(() => {
authService = new AuthService();
vi.clearAllMocks();
});
describe('login', () => {
it('有効な認証情報でトークンを返す', async () => {
const result = await authService.login('user@example.com', 'password123');
expect(result).toHaveProperty('token');
expect(result).toHaveProperty('expiresIn');
});
it('無効な認証情報でエラーをスロー', async () => {
await expect(
authService.login('user@example.com', 'wrongpassword')
).rejects.toThrow('Invalid credentials');
});
});
describe('refreshToken', () => {
it('有効なリフレッシュトークンで新しいトークンを取得', async () => {
const oldToken = await authService.login('user@example.com', 'password123');
vi.useFakeTimers();
vi.advanceTimersByTime(3600000); // 1時間進める
const newToken = await authService.refreshToken(oldToken.token);
expect(newToken.token).not.toBe(oldToken.token);
vi.useRealTimers();
});
});
});
パフォーマンスメトリクスの可視化
bar title テスト実行速度の比較(2026年ベンチマーク)
x-axis [Jest単体テスト, Vitest単体テスト, 統合テスト, Playwrightスモークテスト]
y-axis "実行時間(秒)" 0 --> 300
bar [45, 12, 78, 180]
CI/CD統合|GitHub Actions での実装例
2026年のベストプラクティスでは、段階的なテスト実行を自動化します。
# .github/workflows/test.yml
name: Test Pipeline
on:
push:
branches: [main, develop]
pull_request:
branches: [main, develop]
jobs:
unit-and-integration:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [18.x, 20.x]
steps:
- uses: actions/checkout@v4
- name: Node.js ${{ matrix.node-version }} セットアップ
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
cache: 'npm'
- name: 依存関係をインストール
run: npm ci
- name: Vitest 実行
run: npm run test:unit -- --coverage
- name: Jest 統合テスト実行
run: npm run test:integration
- name: カバレッジレポートアップロード
uses: codecov/codecov-action@v3
with:
files: ./coverage/coverage-final.json
e2e-tests:
runs-on: ubuntu-latest
needs: unit-and-integration
steps:
- uses: actions/checkout@v4
- name: Node.js セットアップ
uses: actions/setup-node@v4
with:
node-version: 20.x
cache: 'npm'
- name: Playwright ブラウザをインストール
run: npx playwright install --with-deps
- name: 依存関係をインストール
run: npm ci
- name: 開発サーバー起動
run: npm run dev &
env:
NEXT_PUBLIC_API_URL: http://localhost:3000
- name: サーバー起動を待機
run: |
npx wait-on http://localhost:3000 --timeout 60000
- name: Playwright E2Eテスト実行
run: npm run test:e2e
- name: テスト結果アップロード
if: always()
uses: actions/upload-artifact@v3
with:
name: playwright-report
path: playwright-report/
retention-days: 30
quality-gates:
runs-on: ubuntu-latest
needs: [unit-and-integration, e2e-tests]
steps:
- uses: actions/checkout@v4
- name: カバレッジ閾値チェック
run: |
COVERAGE=$(cat ./coverage/coverage-summary.json | grep '"lines"' | grep '"pct"' | head -1 | grep -oE '[0-9]+\.[0-9]+')
if (( $(echo "$COVERAGE < 80" | bc -l) )); then
echo "カバレッジが低い: $COVERAGE% < 80%"
exit 1
fi
テスト戦略の最適化テクニック(2026年型)
1. 並列実行の最適化
// vitest.config.ts での並列化設定
export default defineConfig({
test: {
threads: true,
maxThreads: Math.ceil(require('os').cpus().length * 0.8),
minThreads: 1,
sequence: {
sequencer: 'custom',
},
},
});
2. スナップショットテストの実装
// Vitest スナップショット検証
import { render, screen } from '@testing-library/react';
import { describe, it, expect } from 'vitest';
import UserCard from './UserCard';
describe('UserCard スナップショット', () => {
it('レンダリング結果が一致する', () => {
const { container } = render(
<UserCard name="John Doe" role="Developer" />
);
expect(container).toMatchSnapshot();
});
it('ユーザー情報の構造が正しい', () => {
const { container } = render(
<UserCard name="Jane Smith" role="Designer" />
);
expect(container.querySelector('.user-name')).toHaveTextContent('Jane Smith');
});
});
3. カバレッジ最適化
{
"jest": {
"collectCoverageFrom": [
"src/**/*.{js,jsx,ts,tsx}",
"!src/**/*.d.ts",
"!src/**/index.{js,ts}",
"!src/**/__mocks__/**"
],
"coveragePathIgnorePatterns": [
"/node_modules/",
"/dist/",
"/build/"
],
"coverageThresholdForDifferentials": {
"lines": 85,
"functions": 85,
"branches": 80,
"statements": 85
}
}
}
よくある落とし穴と対策
flowchart LR
A["テスト選定"] --> B{"フレームワーク?"}
B -->|大規模プロジェクト| C["Jest 選択"]
B -->|高速フィードバック| D["Vitest 選択"]
B -->|E2E検証| E["Playwright 選択"]
C --> F["デメリット: 遅い"]
D --> G["デメリット: エコシ弱い"]
E --> H["デメリット: 脆弱"]
F --> I["バージョン更新・キャッシュ最適化"]
G --> J["プラグイン探索・カスタム実装"]
H --> K["要素セレクタ堅牢化・待機時間設定"]
落とし穴1:非同期テストのタイムアウト
// ❌ 間違った例
test('データ取得', () => {
fetchData().then(data => {
expect(data).toBeDefined();
});
});
// ✅ 正しい例
test('データ取得', async () => {
const data = await fetchData();
expect(data).toBeDefined();
});
// ✅ コールバック使用時
test('コールバック処理', (done) => {
fetchData((data) => {
expect(data).toBeDefined();
done();
});
}, 10000); // タイムアウト設定
落とし穴2:フレーキーテスト(不安定なテスト)の排除
// ❌ フレーキーなテスト
test('ランダムデータ生成', () => {
const data = Math.random();
expect(data).toBeLessThan(1); // 時々失敗する可能性
});
// ✅ 決定論的なテスト
test('ランダムデータ生成', () => {
vi.stubGlobal('Math', {
random: () => 0.5,
});
const data = Math.random();
expect(data).toBe(0.5);
});
落とし穴3:Playwright セレクタの脆弱性
// ❌ 脆弱なセレクタ
await page.click('button'); // CSSクラスなし、複数存在の可能性
// ✅ 堅牢なセレクタ
await page.click('button[data-testid="submit-btn"]');
await page.click('button:has-text("送信")');
await page.locator('button').filter({ hasText: '送信' }).click();
まとめ
2026年のWeb開発において、効果的なテスト戦略の構築には以下の要素が不可欠です:
-
Jest・Vitest・Playwrightの役割を明確に区分:単体テスト(Jest/Vitest)→統合テスト(Vitest)→E2Eテスト(Playwright)の段階的なテスト設計で、品質と速度を両立させる
-
プロジェクト規模と特性に応じた選択:大規模エンタープライズはJest、高速開発反復はVitest、ユーザー視点の検証はPlaywrightという基本方針を守りつつ、プロジェクトの文脈に適応させる
-
CI/CD パイプラインの段階的実行:軽量な単体テスト → 統合テスト → E2Eテストと段階を踏むことで、CI/CDの効率を最大化し、開発速度を損なわない
-
カバレッジと実行速度のバランス:カバレッジ目標(80%以上)を掲げつつ、テスト実行時間を5分以内に抑える工夫(並列実行、適切なモック)が重要
-
フレーキーテスト排除と保守性確保:決定論的なテスト設計、適切な待機時間設定、データ駆動テストの活用により、長期的に信頼できるテストスイートを構築する
これらの戦略を実装することで、チーム全体で品質を維持しながら、開発速度を加速させることが可能になります。特に2026年は、テストの自動化と AI 支援が一層進むため、早期に戦略を整備することが競争優位性を生み出します。