LabHub

ブログ

フロントエンドテスト 2026 — Playwright / Cypress / Vitest / Jest / Storybook 9 / Chromatic 徹底比較

한국어English日本語

プロローグ — 「Selenium一つで全てを賄った時代は終わった」

2018年頃に「フロントエンドのテストは何を使う?」と聞けば、答えはシンプルだった。ユニットはJest、E2EはSeleniumかCypress、ビジュアルリグレッションは気合のある人だけBackstopJS。それだけだった。「コンポーネント単位のテスト」という言葉はまだぎこちなく、「Storybookがテストインフラ」という発想はなかった。

2026年5月現在、その絵は粉々になった。ある会社のフロントエンドテストパイプラインを描けば、だいたいこんな形だ。

この記事は2026年時点で上記ツールがそれぞれどこに立ち、何が得意で何が苦手か、そして「自分のチームは何を選ぶべきか」を整理する。単なるリストではなく — Playwrightの標準化、Vitestの台頭、Storybook 9の軽量化、AIエージェントがブラウザを直接運転する新しいパラダイムまで — 2024〜2026年の間にこの市場を揺らした4つの流れを一緒に見る。


1章・2026年のフロントエンドテスト地図 — Unit / Component / E2E / Visualの4軸

まずは大局。フロントエンドテストは4つの軸に分かれる。

何を検証するか代表ツール
Unit関数、フック、ユーティリティ — 最速のフィードバックVitest 3、Jest 30、Mocha
Componentコンポーネント単位のレンダリング・相互作用Testing Library、Storybook 9 + Vitest、Playwright CT
E2E(End-to-End)ユーザーフロー、複数ページPlaywright、Cypress 14、WebdriverIO 9、Selenium 5
Visual Regressionピクセル単位のUI変化検出Chromatic、Percy、Applitools、Loki、BackstopJS、Reg-Suit

ここに2024〜2026年で新たに加わったカテゴリが二つある。

そして2026年のトレンドは明白だ。「一つのツールで複数の軸を束ねる」。PlaywrightはE2E + Component + Visualを一括で提供し、Vitestは(Storybookと共に)Unit + Componentを束ねた。Cypressも同様の統合を試みるが速度で押し負ける。点ツールから始めてプラットフォーム化するか、最初からプラットフォームとして来るかの二択。

テスティングピラミッド(ユニットが多く、E2Eが少ない)は今も有効だが — 2026年にはその形が変形した。Mike Cohnのクラシックなピラミッドはコンポーネント層が抜けて「ダイヤモンド」または「トロフィー」型になった。コンポーネントテストが爆発的に増え、ユニットテストはドメインロジック中心に軽くなり、コンポーネントテストが真ん中を埋める。


2章・Playwright — 事実上の標準(VS Code統合、Trace Viewer)

PlaywrightはMicrosoftが作ったE2Eテストツールで、2020年の1.0リリース以降急速に市場を制した。2026年5月時点で1.50+。State of JS 2024調査では使用意向の最も高いE2Eツールに選ばれた。

コア概念

強み

弱み

いつ選ぶか

// playwright.config.ts
import { defineConfig, devices } from '@playwright/test'

export default defineConfig({
  testDir: './e2e',
  fullyParallel: true,
  retries: process.env.CI ? 2 : 0,
  reporter: [['html'], ['github']],
  use: {
    baseURL: 'http://localhost:3000',
    trace: 'on-first-retry',
    screenshot: 'only-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 7'] } },
  ],
})
// e2e/checkout.spec.ts
import { test, expect } from '@playwright/test'

test('user can complete checkout', async ({ page }) => {
  await page.goto('/products/coffee-beans')
  await page.getByRole('button', { name: 'Add to cart' }).click()
  await page.getByRole('link', { name: 'Cart' }).click()
  await expect(page.getByText('Coffee Beans')).toBeVisible()
  await page.getByRole('button', { name: 'Checkout' }).click()
  await page.getByLabel('Email').fill('test@example.com')
  await page.getByRole('button', { name: 'Place order' }).click()
  await expect(page).toHaveURL(/thank-you/)
})

Playwrightの本当の価値はTrace Viewerだ。CIで失敗したテストのtrace.zipを取得してローカルで開けば、各アクションのDOMスナップショット・ネットワーク呼び出し・コンソールログがGUIタイムラインで再生される。「なぜ失敗した?」がキャプチャで終わる。Cypressのビデオ録画の一段上だ。


3章・Cypress 14 — 依然強力

Cypressは2017年頃に登場し、しばらくの間E2Eの標準だった。2026年5月時点で14。シェアはPlaywrightに押されたが、依然大きなユーザーベースを持つ。

コア概念

強み

弱み

いつ選ぶか

// cypress/e2e/login.cy.js
describe('Login flow', () => {
  beforeEach(() => {
    cy.intercept('POST', '/api/login', { fixture: 'login-success.json' }).as('login')
    cy.visit('/login')
  })

  it('logs in with valid credentials', () => {
    cy.get('[data-cy=email]').type('user@example.com')
    cy.get('[data-cy=password]').type('s3cret!')
    cy.get('[data-cy=submit]').click()
    cy.wait('@login')
    cy.url().should('include', '/dashboard')
    cy.contains('Welcome back').should('be.visible')
  })
})

Cypress 14で追加された最大の機能はWebKit GAComponent Testing UX改善だ。だがPlaywrightが既に一歩先を行っており、新規プロジェクトがCypressを選ぶ割合は2024年から急速に下がっている。


4章・Vitest 3 — Viteと共に来る最速

Vitestは2021年Anthony Fuが作ったユニットテストランナーで、Viteのトランスフォーマー・HMRをそのまま活用する。2026年時点で3.x、JS / TS新規プロジェクトのユニットテスト標準となった。

コア概念

強み

弱み

いつ選ぶか

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

export default defineConfig({
  plugins: [react()],
  test: {
    globals: true,
    environment: 'happy-dom',
    setupFiles: ['./test/setup.ts'],
    coverage: {
      provider: 'v8',
      reporter: ['text', 'json', 'html'],
      exclude: ['node_modules/', 'test/'],
    },
  },
})
// src/utils/format.test.ts
import { describe, it, expect } from 'vitest'
import { formatCurrency } from './format'

describe('formatCurrency', () => {
  it('formats JPY without decimals', () => {
    expect(formatCurrency(12345, 'JPY')).toBe('¥12,345')
  })

  it('formats USD with two decimals', () => {
    expect(formatCurrency(12.5, 'USD')).toBe('$12.50')
  })

  it('handles negative numbers', () => {
    expect(formatCurrency(-100, 'USD')).toBe('-$100.00')
  })
})

Vitest 3の最大の変化はBrowser mode GAだ。JSDOMではなく実Chromiumでコンポーネントをマウントしてテストする。Playwright Component Testingと似た領域だが、Viteベースのプロジェクトではより自然。


5章・Jest 30 — レガシー + Next互換

JestはFacebookが2014年に作ったユニットテストランナーで、しばらくの間JSテストの事実上の標準だった。2025年9月に30がリリースされ、2026年5月には30.xマイナーリリースが安定段階にある。

コア概念

強み

弱み

いつ選ぶか

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

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

const customConfig = {
  setupFilesAfterEach: ['<rootDir>/jest.setup.js'],
  testEnvironment: 'jsdom',
  moduleNameMapper: {
    '^@/(.*)$': '<rootDir>/src/$1',
  },
  collectCoverageFrom: [
    'src/**/*.{ts,tsx}',
    '!src/**/*.stories.tsx',
  ],
}

module.exports = createJestConfig(customConfig)

Jest 30の最大の変化は初のnative ESMサポート安定化メモリ使用量30%削減。移行コストのため依然大組織ではJestを使うが、新規プロジェクトでJestを選ぶことは少なくなっている。


6章・Testing Libraryの哲学 — Implementation detailを避ける

Testing Libraryは2018年にKent C. Doddsが作ったライブラリ群だ。DOM、React、Vue、Svelte、Solidそれぞれのアダプターがある。2026年時点で全てのコンポーネントテストの基礎。

中核哲学

強み

弱み

例 — React Testing Library + Vitest

import { describe, it, expect } from 'vitest'
import { render, screen } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import { LoginForm } from './LoginForm'

describe('LoginForm', () => {
  it('shows error when password is too short', async () => {
    const user = userEvent.setup()
    render(<LoginForm onSubmit={() => {}} />)

    await user.type(screen.getByLabelText(/email/i), 'user@example.com')
    await user.type(screen.getByLabelText(/password/i), 'abc')
    await user.click(screen.getByRole('button', { name: /sign in/i }))

    expect(
      await screen.findByText(/password must be at least 8 characters/i)
    ).toBeInTheDocument()
  })

  it('calls onSubmit with valid input', async () => {
    const onSubmit = vi.fn()
    const user = userEvent.setup()
    render(<LoginForm onSubmit={onSubmit} />)

    await user.type(screen.getByLabelText(/email/i), 'user@example.com')
    await user.type(screen.getByLabelText(/password/i), 'longerpassword')
    await user.click(screen.getByRole('button', { name: /sign in/i }))

    expect(onSubmit).toHaveBeenCalledWith({
      email: 'user@example.com',
      password: 'longerpassword',
    })
  })
})

よく陥る罠 — screen.getByText("Loading...")のようなテキスト完全一致はi18nで壊れる。getByRole("status")または正規表現が安全。そしてdata-testid="submit-button"は本当に最終手段としてのみ。ユーザーはtestidを見ない。


7章・Storybook 9(2025年6月) — 軽量化しVitestと統合

Storybookは2016年に登場したコンポーネントワークショップだ。2025年6月にリリースされた9バージョンは大きな転換点だった。軽くなり(バンドルサイズ約50%減)、Vitestと深く統合された。

主要変化(Storybook 9)

強み

弱み

例 — CSF 3 + Play function

// Button.stories.tsx
import type { Meta, StoryObj } from '@storybook/react'
import { expect, userEvent, within } from '@storybook/test'
import { Button } from './Button'

const meta: Meta<typeof Button> = {
  title: 'UI/Button',
  component: Button,
  tags: ['autodocs'],
}
export default meta

type Story = StoryObj<typeof Button>

export const Primary: Story = {
  args: {
    variant: 'primary',
    children: 'Click me',
  },
}

export const Clicked: Story = {
  args: { variant: 'primary', children: 'Click me' },
  play: async ({ canvasElement }) => {
    const canvas = within(canvasElement)
    const btn = canvas.getByRole('button', { name: /click me/i })
    await userEvent.click(btn)
    await expect(btn).toHaveAttribute('aria-pressed', 'true')
  },
}
// vitest.workspace.ts — Storybook 9 + Vitest統合
import { defineWorkspace } from 'vitest/config'
import { storybookTest } from '@storybook/addon-vitest/vitest-plugin'

export default defineWorkspace([
  './vitest.config.ts',
  {
    extends: './vitest.config.ts',
    plugins: [storybookTest({ configDir: '.storybook' })],
    test: {
      name: 'storybook',
      browser: {
        enabled: true,
        headless: true,
        name: 'chromium',
        provider: 'playwright',
      },
    },
  },
])

Storybook 9の真の価値は「ストーリーを一度書けばカタログ・テスト・ビジュアルリグレッション・アクセシビリティが全部ついてくる」点だ。デザインシステムチームなら事実上必須。


8章・Chromatic / Percy / Applitools — ビジュアルリグレッション

ビジュアルリグレッション(Visual Regression)はUI変更が意図したものか否かをピクセル単位で検査する。SaaSの三強はChromatic、Percy(BrowserStack)、Applitoolsだ。

Chromatic

Percy(BrowserStack)

Applitools

三者の違い

項目ChromaticPercyApplitools
母体StorybookBrowserStack独立SaaS
強みStorybook統合複数viewport、BSデバイスAI Visual diff
価格モデルsnapshot基準snapshot + DOM基準enterprise seat
Free tier寛大(月5,000 snapshot)小さい評価版のみ
AI処理部分的部分的中核機能
// Playwright + Percy
import { test } from '@playwright/test'
import percySnapshot from '@percy/playwright'

test('homepage looks correct', async ({ page }) => {
  await page.goto('/')
  await percySnapshot(page, 'Homepage')
})
// Playwright + Applitools Eyes
import { test } from '@playwright/test'
import { Eyes, BatchInfo, Configuration } from '@applitools/eyes-playwright'

test('checkout flow visual', async ({ page }) => {
  const eyes = new Eyes()
  const cfg = new Configuration()
  cfg.setBatch(new BatchInfo('Smoke 2026-05-16'))
  eyes.setConfiguration(cfg)
  await eyes.open(page, 'Shop', 'Checkout')
  await page.goto('/checkout')
  await eyes.check('Checkout page', undefined)
  await eyes.close()
})

ビジュアルリグレッションの最大の罠はflaky snapshotだ。フォントロード、アニメーション、カルーセル、広告 — このような動的要素をマスキングしなければ95%のdiffがfalse positive。Applitoolsが強い理由はこれを自動で処理するから。


9章・Loki / BackstopJS / Reg-Suit — オープンソースのビジュアルリグレッション

SaaSが負担になる、あるいはベースラインを自分のリポジトリに置きたいならOSSの選択肢がある。

Loki

BackstopJS

Reg-Suit

三者の違い

項目LokiBackstopJSReg-Suit
母体Storybookエコシステム独立日本OSS
ベースライン保存gitローカルS3 / GCS
キャプチャエンジンChromiumPuppeteer外部(Playwrightなど)
PR統合直接弱い強い(GitHub bot)
// backstop.json — BackstopJSの基本設定
{
  "id": "my-project",
  "viewports": [
    { "label": "mobile", "width": 375, "height": 667 },
    { "label": "desktop", "width": 1920, "height": 1080 }
  ],
  "scenarios": [
    {
      "label": "Homepage",
      "url": "http://localhost:3000/",
      "delay": 500,
      "misMatchThreshold": 0.1
    }
  ],
  "engine": "puppeteer",
  "report": ["browser"]
}

OSSの強みはベースラインを自インフラに置くこと。セキュリティが厳格な金融・ヘルスケアではSaaSを使えない場合が多く、Reg-Suitまたはセルフホストが好まれる。


10章・Browser MCP + Playwright MCP — AIエージェントがブラウザを運転

2024年後半から大きな変化があった。MCP(Model Context Protocol)が標準化され、AIエージェントが直接ブラウザを運転するパターンが一般化した。2026年5月時点でPlaywright MCPとBrowser MCPの二陣営がある。

Playwright MCP(Microsoft)

Browser MCP

使用パターン

// .cursor/mcp.json — Playwright MCPを登録
{
  "mcpServers": {
    "playwright": {
      "command": "npx",
      "args": ["@playwright/mcp@latest"]
    }
  }
}

この設定後にAIエージェントに「チェックアウトフローのテストを書いて実行して」と頼めば、エージェントが直接ブラウザを開いてクリック・入力しながら*.spec.tsを生成する。Codegenの次世代。

限界

そこで2026年のパターンは「エージェントが草案を作成 → 人がレビュー + 安定化 → CIに登録」だ。エージェントが毎CIで回るのはまだ先。


11章・MSW(Mock Service Worker) + ネットワークモッキング戦略

MSWは2019年Artem Zakharchenkoが作ったネットワークモッキングライブラリだ。2026年時点で2.x、フロントエンドテストの事実上の標準ネットワークモッキングツールとなった。

コア概念

強み

例 — MSWハンドラ

// mocks/handlers.ts
import { http, HttpResponse } from 'msw'

export const handlers = [
  http.get('/api/products', () => {
    return HttpResponse.json([
      { id: 1, name: 'Coffee Beans', price: 25000 },
      { id: 2, name: 'Tea Set', price: 35000 },
    ])
  }),

  http.post('/api/cart', async ({ request }) => {
    const body = await request.json()
    return HttpResponse.json({ ok: true, cartId: 'abc-123' }, { status: 201 })
  }),

  http.get('/api/user/me', () => {
    return new HttpResponse(null, { status: 401 })
  }),
]
// test/setup.ts — Vitestで
import { setupServer } from 'msw/node'
import { handlers } from '../mocks/handlers'
import { afterAll, afterEach, beforeAll } from 'vitest'

const server = setupServer(...handlers)

beforeAll(() => server.listen({ onUnhandledRequest: 'error' }))
afterEach(() => server.resetHandlers())
afterAll(() => server.close())
// browser entrypoint(開発環境)
import { setupWorker } from 'msw/browser'
import { handlers } from './mocks/handlers'

if (process.env.NODE_ENV === 'development') {
  const worker = setupWorker(...handlers)
  worker.start()
}

MSWの真の価値は「テスト・開発・Storybookで同じモッキングコードを使う」点だ。バックエンドがまだないときもフロントエンドが進行可能、テストと実開発が同じfixtureを共有。

代替としてPlaywrightのpage.route()、Cypressのcy.intercept()もあるが — 各ツール専用で再利用性が低い。そこでMSWを下層に置き、E2Eではその上にPlaywright routeを乗せるハイブリッドがよくある。


12章・Page Object / Component Testingパターン

E2Eテストが増えるとコード重複が問題となる。そこで2026年の標準パターンは二つ。

Page Object Model(POM)

// e2e/pages/CheckoutPage.ts
import { Page, Locator, expect } from '@playwright/test'

export class CheckoutPage {
  readonly page: Page
  readonly emailInput: Locator
  readonly submitButton: Locator

  constructor(page: Page) {
    this.page = page
    this.emailInput = page.getByLabel('Email')
    this.submitButton = page.getByRole('button', { name: 'Place order' })
  }

  async goto() {
    await this.page.goto('/checkout')
  }

  async fillEmail(email: string) {
    await this.emailInput.fill(email)
  }

  async submit() {
    await this.submitButton.click()
  }

  async expectSuccess() {
    await expect(this.page).toHaveURL(/thank-you/)
  }
}
// e2e/checkout.spec.ts
import { test } from '@playwright/test'
import { CheckoutPage } from './pages/CheckoutPage'

test('user completes checkout', async ({ page }) => {
  const checkout = new CheckoutPage(page)
  await checkout.goto()
  await checkout.fillEmail('user@example.com')
  await checkout.submit()
  await checkout.expectSuccess()
})

Component Testing

どのパターンをいつ

POMの罠 — 抽象化しすぎるとテストが何をしているか見えない。「Login → Cart → Checkout」が一行で終われば読みやすいが失敗解析が難しい。適度な抽象化 + Playwright Trace Viewerの組合せが最適。


13章・韓国 / 日本の事例 — トス、カカオ、メルカリ

韓国 — トスのUIテスト

トスは100名以上のフロントエンドチームを持つ大組織で、自前のデザインシステム(Toss DS)を運営する。公開ブログ・発表資料に表れるパターンは以下の通り。

トスブログの「フロントエンドにテストは本当に必要か?」のような記事に見える哲学は — 「テストはデザインシステムと一体」。コンポーネント単位が強ければページ単位は少なく。

韓国 — カカオのフロントエンド

カカオ陣営(カカオ、カカオバンク、カカオエンタープライズ)も類似スタックだ。

日本 — メルカリのコンポーネントテスティング

メルカリはStorybook + Vitest + Reg-Suitの組合せを公開的に発表したことがある。パターンは以下の通り。

メルカリ Engineering Blogのビジュアルリグレッション記事がReg-Suitの実使用事例としてよく引用される。日本でReg-Suitが強い理由の一つがメルカリの採用。

結論 — 地域別推奨

シナリオ韓国推奨日本推奨
スタートアップ(10〜50人)Vitest + Playwright + ChromaticVitest + Playwright + Reg-Suit
中堅(50〜500人)Storybook 9 + Chromatic + PlaywrightStorybook 9 + Reg-Suit + Playwright
大企業(500+)自前デザインシステム + Chromatic + Playwright自前 + Reg-Suit + Playwright
金融自前ホストReg-Suit + Playwright自前ホスト + Selenium 5残存
グローバルSaaSApplitools + PlaywrightApplitools + Playwright

14章・どのスタックを選ぶべきか — シナリオ別ガイド

シナリオA — 大規模SaaS(数十ページ、多言語、複数ブラウザ)

シナリオB — デザインシステム / コンポーネントライブラリ

シナリオC — E-commerce(カート、決済、複数ページ)

シナリオD — メディア / コンテンツサイト

シナリオE — レガシー移行(Jest + Cypress → Vitest + Playwright)

コスト見積もり(2026年5月基準、50人フロントエンドチーム)

ツール無料tier有料(月額)
Playwright無料
Vitest無料
Storybook無料
Chromatic月5,000 snapshot無料149149〜649
Percy月5,000 snapshot無料約$199+
Applitools評価版enterprise quote
Cypress Cloud月500結果無料7575〜300+
MSW無料
Loki / BackstopJS / Reg-Suit無料

小規模チームの出発点は単純だ — Vitest + Playwright + Chromatic無料tier。ここから始めてチームが大きくなればStorybook 9をデザインシステム中心に、ビジュアルリグレッションを有料tierに、そしてPlaywright MCPを導入して新規テスト作成速度を上げるのが標準ルート。


おわりに — 「ツールを買うのではなく、信頼を買うのだ」

2026年のフロントエンドテスティング市場は単一ツールのトーナメントではなく4軸の合奏だ。そして次の5つの流れが市場を揺らしている。

  1. Playwrightの標準化 — 新規E2Eの70%+シェア。時間が経つほど格差拡大。
  2. Vitestの台頭 — Viteエコシステムと共に事実上の標準ユニットランナー。
  3. Storybook 9の軽量化 — デザインシステム + コンポーネントテスト + ビジュアルリグレッションのハブ。
  4. AIエージェントが直接テスト作成 — Playwright MCP、Browser MCPが日常化。
  5. MSWの標準化 — ネットワークモッキング = MSW。ツール独立なfixture。

小規模チームの初行 — npm i -D vitest @testing-library/react @playwright/test msw。この4つで95%のケースがカバーされる。次のステップがStorybook 9 + Chromatic、その次がPlaywright MCP統合。

一つの真理 — 「ツールを買うのではなく、信頼(テストが壊れた時に本当にバグを捕まえるという信頼)を買うのだ」。どのツールを選んでもその信頼が90%未満なら、そのテストはPRを遮断するノイズに過ぎない。ツール評価の最初の質問は常に「このテストが赤線を出した時、実際にバグを捕まえた比率は?」だ。


参考 / References

コメント

まだコメントはありません。

ログインするとコメントできます