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 コンポーネント、モダンプロジェクト
PlaywrightE2Eテスト低速(秒単位)アプリケーション全体ユーザーフロー、本番検証

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 支援が一層進むため、早期に戦略を整備することが競争優位性を生み出します。

U

Untanbaby

ソフトウェアエンジニア|AWS / クラウドアーキテクチャ / DevOps

10年以上のIT実務経験をもとに、現場で使える技術情報を発信しています。 記事の誤りや改善点があればお問い合わせからお気軽にご連絡ください。

関連記事