AI

OSSのテストフレームワーク比較:Vitest vs Jest vs Playwright でフロントエンドテストを高速化する

オープンソースラボ編集部2026年6月13日

OSSのテストフレームワーク比較:Vitest vs Jest vs Playwright でフロントエンドテストを高速化する

Cypress(月$75/チーム〜)に対して、Vitest(最速のVite Native Unit Test)・Jest(最もポピュラーなJavaScriptテストランナー)・Playwright(Microsoftが開発したE2Eブラウザテスト)はOSSのJavaScript/TypeScriptテストフレームワークです。

テストの種類とカバレッジ

Unit Test     → Vitest / Jest
Integration   → Vitest + Testing Library
E2E Test      → Playwright / Cypress
API Test      → Vitest + msw / Supertest
Visual Test   → Playwright / Storybook

主要ツールの概要

Vitest

2022年に公開されたVite専用の高速テストランナーです。GitHubスター13k+。Jestと互換性のあるAPIでViteプロジェクトにネイティブ統合し、起動時間がJestの1/10〜1/100程度です。TypeScript/ESM/CJSをトランスフォームなしで実行でき、vi.mock()・スナップショット・カバレッジ(v8/istanbul)・ブラウザモード(Vitest Browser Mode)・UIダッシュボードを提供します。

# Next.js プロジェクトにVitestをセットアップ
npm install -D vitest @vitejs/plugin-react @testing-library/react @testing-library/jest-dom @testing-library/user-event jsdom
// vitest.config.ts - Vitest設定
import { defineConfig } from "vitest/config";
import react from "@vitejs/plugin-react";
import { resolve } from "path";

export default defineConfig({
  plugins: [react()],
  test: {
    environment: "jsdom",
    globals: true,               // expect, describe, it を import なしで使う
    setupFiles: ["./src/test/setup.ts"],
    coverage: {
      provider: "v8",
      reporter: ["text", "json", "html"],
      exclude: ["node_modules", "src/test"],
    },
    exclude: ["tests/e2e/**"],   // PlaywrightのE2Eは除外
  },
  resolve: {
    alias: { "@": resolve(__dirname, "./src") },
  },
});

// src/test/setup.ts
import "@testing-library/jest-dom";
import { vi } from "vitest";

// fetch のモック(テスト環境にはfetchがない)
global.fetch = vi.fn();
// src/components/PostCard.test.tsx - React Componentのテスト
import { render, screen, fireEvent } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { describe, it, expect, vi, beforeEach } from "vitest";
import { PostCard } from "./PostCard";

const mockPost = {
  id: "1",
  title: "OSSのテスト戦略",
  slug: "oss-testing-strategy",
  excerpt: "Vitest・Playwright・Jestの使い分けを解説",
  author: { name: "山田太郎" },
  publishedAt: "2026-01-01",
};

describe("PostCard", () => {
  it("タイトルと著者が表示される", () => {
    render(<PostCard post={mockPost} />);
    expect(screen.getByText("OSSのテスト戦略")).toBeInTheDocument();
    expect(screen.getByText("山田太郎")).toBeInTheDocument();
  });

  it("記事ページへのリンクが正しい", () => {
    render(<PostCard post={mockPost} />);
    const link = screen.getByRole("link");
    expect(link).toHaveAttribute("href", "/blog/oss-testing-strategy");
  });

  it("いいねボタンをクリックするとカウントが増える", async () => {
    const onLike = vi.fn();
    render(<PostCard post={mockPost} onLike={onLike} />);
    await userEvent.click(screen.getByRole("button", { name: /いいね/ }));
    expect(onLike).toHaveBeenCalledWith("1");
    expect(onLike).toHaveBeenCalledTimes(1);
  });
});
// src/lib/posts.test.ts - Server Functionのユニットテスト(DBモック)
import { describe, it, expect, vi, beforeEach } from "vitest";
import { getPublishedPosts, createPost } from "@/lib/posts";

// DBモジュールをモック
vi.mock("@/lib/db", () => ({
  db: {
    query: {
      posts: {
        findMany: vi.fn(),
        findFirst: vi.fn(),
      },
    },
    insert: vi.fn(),
    update: vi.fn(),
  },
}));

import { db } from "@/lib/db";

describe("getPublishedPosts", () => {
  beforeEach(() => {
    vi.clearAllMocks();
  });

  it("公開された記事のみを返す", async () => {
    const mockPosts = [
      { id: "1", title: "記事1", status: "published" },
      { id: "2", title: "記事2", status: "published" },
    ];
    vi.mocked(db.query.posts.findMany).mockResolvedValue(mockPosts);

    const result = await getPublishedPosts(1, 10);

    expect(result).toHaveLength(2);
    expect(db.query.posts.findMany).toHaveBeenCalledWith(
      expect.objectContaining({ where: expect.anything() })
    );
  });
});

Playwright

2020年にMicrosoftが公開したOSSのブラウザ自動化・E2Eテストフレームワークです。GitHubスター68k+。Chromium・Firefox・Safari(WebKit)の3ブラウザで並列テストが可能で、TypeScriptファースト・自動待機(flaky test防止)・スクリーンショット・ビデオ録画・トレービューア・APIテストをサポート。playwright codegenでブラウザ操作からテストコードを自動生成できます。

# Playwrightのインストールとセットアップ
npm install -D @playwright/test
npx playwright install  # Chromium・Firefox・WebKitをダウンロード
// playwright.config.ts - Playwright設定
import { defineConfig, devices } from "@playwright/test";

export default defineConfig({
  testDir: "./tests/e2e",
  fullyParallel: true,           // テストを並列実行
  forbidOnly: Boolean(process.env.CI),
  retries: process.env.CI ? 2 : 0,  // CI環境では2回リトライ
  workers: process.env.CI ? 1 : undefined,
  reporter: [["html", { outputFolder: "playwright-report" }]],
  use: {
    baseURL: "http://localhost:3000",
    trace: "on-first-retry",    // 失敗時にトレースを保存
    screenshot: "on",
    video: "retain-on-failure",
  },
  projects: [
    { name: "chromium", use: { ...devices["Desktop Chrome"] } },
    { name: "firefox", use: { ...devices["Desktop Firefox"] } },
    { name: "safari", use: { ...devices["Desktop Safari"] } },
    { name: "mobile-chrome", use: { ...devices["Pixel 5"] } },
  ],
  webServer: {
    command: "npm run dev",
    url: "http://localhost:3000",
    reuseExistingServer: !process.env.CI,
  },
});
// tests/e2e/auth.spec.ts - 認証フローのE2Eテスト
import { test, expect } from "@playwright/test";

test.describe("認証フロー", () => {
  test("メールアドレスとパスワードでログインできる", async ({ page }) => {
    await page.goto("/login");

    // フォームに入力
    await page.getByLabel("メールアドレス").fill("test@example.com");
    await page.getByLabel("パスワード").fill("TestPassword123");
    await page.getByRole("button", { name: "ログイン" }).click();

    // ログイン後のリダイレクト確認
    await expect(page).toHaveURL("/dashboard");
    await expect(page.getByRole("heading", { name: "ダッシュボード" })).toBeVisible();
  });

  test("間違ったパスワードでエラーが表示される", async ({ page }) => {
    await page.goto("/login");
    await page.getByLabel("メールアドレス").fill("test@example.com");
    await page.getByLabel("パスワード").fill("wrongpassword");
    await page.getByRole("button", { name: "ログイン" }).click();

    await expect(page.getByText("メールアドレスまたはパスワードが正しくありません")).toBeVisible();
    await expect(page).toHaveURL("/login");
  });
});

// tests/e2e/blog.spec.ts - 記事一覧・詳細のE2Eテスト
test.describe("ブログ", () => {
  test("記事一覧から詳細ページへ遷移できる", async ({ page }) => {
    await page.goto("/blog");
    const firstArticle = page.getByRole("article").first();
    const title = await firstArticle.getByRole("heading").textContent();
    await firstArticle.getByRole("link").first().click();
    await expect(page.getByRole("heading", { name: title! })).toBeVisible();
  });

  test("モバイルレイアウトが正しく表示される", async ({ page }) => {
    await page.setViewportSize({ width: 375, height: 812 });
    await page.goto("/blog");
    // ハンバーガーメニューが表示される
    await expect(page.getByRole("button", { name: "メニュー" })).toBeVisible();
    // デスクトップナビが非表示
    await expect(page.getByRole("navigation", { name: "main" })).not.toBeVisible();
  });
});

機能比較表

比較項目VitestJestPlaywright
対象テストUnit/IntegrationUnit/IntegrationE2E
起動速度最速(Viteネイティブ)遅い(変換あり)-
ブラウザテストブラウザモード(実験)jsdom(模擬)✅(実ブラウザ)
TypeScriptネイティブBabel変換✅(ネイティブ)
並列実行✅(3ブラウザ並列)
GitHub Stars13k+44k+68k+

テストを自動実行するCI/CDパイプラインにはDevOpsカテゴリ/categories/devopsのGitHub ActionsでPull RequestごとにVitest+Playwrightを実行します。テスト結果をSlackに通知するにはCommunicationカテゴリ/categories/communicationのSlack Webhookと組み合わせます。

FAQ

Q. VitestとJestはどちらを新規プロジェクトで採用すべきですか?

A. 新規Vite/Next.js(Viteベース)プロジェクトはVitestを推奨します。理由: ①起動時間がJestの1/10以下(npx vitestが1秒以下で起動)②TypeScriptをトランスフォームなしで実行できる③vi.mock()vi.spyOn()等のAPIがJestと互換性がある(既存Jestコードの移行が楽)④HMR的なウォッチモードでテスト変更を即座にフィードバック。Jestを使い続けるケース: ①既存のJestテストが大量にある②create-react-app(webpack)ベース③Babel変換が必要な特殊なプロジェクト。2026年時点では新規プロジェクトはVitestがデファクトスタンダードになっています。

Q. PlaywrightでCIのflakyテスト(不安定なテスト)を防ぐには?

A. ①自動待機: page.getByRole("button").click()はPlaywrightが自動で要素が見えるまで待機するためpage.waitForTimeout()は避ける②ロケーターの安定性: page.locator(".btn-123abc")(クラス名)ではなくpage.getByRole("button", { name: "送信" })(役割)を使う③テストの独立性: 各テストが独立したステート(DBやCookie)を持つようにして順序依存をなくす④リトライ設定: playwright.config.tsでCI時にretries: 2を設定⑤トレースビューア: 失敗時のトレース(trace.zip)をCIのアーティファクトに保存してデバッグを効率化。

Q. Playwrightのcodegenでテストコードを自動生成するには?

A. npx playwright codegenコマンドで起動するとブラウザが開き、操作をコードに変換してくれます。

# ローカルサーバーに対してcodegen実行
npx playwright codegen http://localhost:3000

# 認証済み状態でcodegen(ログイン後の状態を保存)
npx playwright codegen --save-storage=auth.json http://localhost:3000/login
# ログイン操作後にブラウザを閉じると auth.json に保存
npx playwright codegen --load-storage=auth.json http://localhost:3000/dashboard

Q. Next.js App RouterのServer Actionをテストするには?

A. Server ActionはNode.jsサーバーで実行されるため、IntegrationテストはVitestで直接関数を呼び出す形で書けます("use server"ディレクティブはテスト環境では無視される)。実際のHTTPリクエストレベルでテストするにはPlaywrightのAPIテスト機能(requestフィクスチャ)またはSupertestを使います。

// Vitest でServer Actionを直接テスト
import { createPostAction } from "@/app/blog/new/actions";
vi.mock("@/auth", () => ({ auth: vi.fn(() => ({ user: { id: "user-1" } })) }));
vi.mock("@/lib/db");

it("認証済みユーザーが記事を作成できる", async () => {
  const fd = new FormData();
  fd.append("title", "テスト記事");
  fd.append("content", "テスト本文");
  await expect(createPostAction(fd)).resolves.not.toThrow();
});

まとめ

ユースケース推奨ツール
Unit/IntegrationテストのVite環境Vitest
既存Jest資産・非ViteプロジェクトJest
E2Eブラウザテスト・マルチブラウザPlaywright

関連外部リソース

他の記事も読む

Let's Build Together

OSS導入、自社だけで悩まない。

ツール選定から構築・運用・AI活用まで、オープンソースラボ運営元のClasslessが伴走します。初回のご相談は無料です。