SvelteKitにPlaywrightでE2Eテストを導入した話

PlaywrightはE2Eテスト(End-to-End)のツールです。実際のブラウザを動かしてユーザー操作をシミュレートします。Vitestのユニットテストと組み合わせることで、ロジックから画面全体まで幅広くテストできます。


VitestとPlaywrightの違い

Vitest(ユニットテスト)
  → 関数やコンポーネントを単体でテスト
  → ブラウザ不要・高速
  → 例:「formatDate('2026-05-19') が '2026年5月19日' を返すか」

Playwright(E2Eテスト)
  → 実際のブラウザでユーザー操作をシミュレート
  → サーバーも含めた全体の動作をテスト
  → 例:「ログインしてトップページに記事一覧が表示されるか」
Vitest Playwright
テスト対象 関数・コンポーネント単体 ブラウザ全体の動作
速度 高速 比較的遅い
向いているケース ロジックのテスト ユーザー操作・画面遷移のテスト
DBやサーバー 不要 実際のサーバーが必要

① インストール

npm install -D @playwright/test
npx playwright install chromium

chromium だけインストールするとダウンロードが速く済みます。


playwright.config.ts を作成

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

export default defineConfig({
  testDir: './e2e',
  testMatch: '**/*.test.ts',
  use: {
    baseURL: 'http://localhost:8787',  // wrangler dev のポート
  },
  webServer: {
    command: 'npx wrangler dev',
    url: 'http://localhost:8787',
    reuseExistingServer: true,  // 起動済みのサーバーを再利用
  },
});

ポイント: wrangler dev のデフォルトポートは 8787 です。reuseExistingServer: true を設定しておくと、別ターミナルで起動済みのサーバーをそのまま使ってくれます。

推奨する実行手順:

# ターミナル1:サーバーを起動
npx wrangler dev

# ターミナル2:テストを実行
npx playwright test

③ テストを書く

// e2e/top.test.ts
import { test, expect } from '@playwright/test';

test('トップページに記事一覧が表示される', async ({ page }) => {
  await page.goto('/');

  await expect(page.getByRole('heading', { name: '記事一覧' })).toBeVisible();
  const links = page.getByRole('link');
  await expect(links.first()).toBeVisible();
});

test('記事をクリックすると詳細ページに遷移する', async ({ page }) => {
  await page.goto('/');

  await page.getByRole('link').first().click();

  await expect(page).toHaveURL(/\/posts\/\d+/);
});

④ よく使う操作

// ページ遷移
await page.goto('/login');

// テキスト入力
await page.getByLabel('メールアドレス').fill('test@example.com');

// ボタンクリック
await page.getByRole('button', { name: 'ログイン' }).click();

// 表示確認
await expect(page.getByText('ようこそ')).toBeVisible();

// URL確認
await expect(page).toHaveURL('/dashboard');

// スクリーンショット(デバッグ用)
await page.screenshot({ path: 'screenshot.png' });

⑤ セレクターの使い分け(getByRole / getByText / getByLabel)

Playwrightには要素を探すセレクターが複数あります。基本は getByRole を優先して使い、それで取れない場合に他を使うのが推奨スタイルです。

getByRole — HTML の役割で探す(最も推奨)

page.getByRole('heading', { name: '記事一覧' })  // <h1>〜<h6>
page.getByRole('button', { name: 'ログイン' })   // <button>
page.getByRole('link', { name: 'トップ' })       // <a>
page.getByRole('textbox', { name: 'メール' })    // <input type="text">
page.getByRole('checkbox')                       // <input type="checkbox">

HTMLのセマンティクス(意味)で要素を探すため、CSSクラスが変わっても壊れにくいのが強みです。

getByText — 表示テキストで探す

page.getByText('ようこそ')
page.getByText('カウント: 0')
page.getByText(/エラー/)  // 正規表現も使える

ボタンや見出し以外のテキストコンテンツを探すときに使います。

getByLabel — フォームのラベルで探す

page.getByLabel('メールアドレス')  // <label>と紐づいた<input>を取得
page.getByLabel('パスワード')

<label><input> が正しく紐づいているフォームで使います。

<!-- この構造のとき getByLabel('メールアドレス') で input を取得できる -->
<label for="email">メールアドレス</label>
<input id="email" type="email" />

使い分けの基準

セレクター 向いているケース
getByRole ボタン・リンク・見出しなど(基本これを使う)
getByText 段落・メッセージなどボタン以外のテキスト
getByLabel フォームの入力欄
getByPlaceholder labelがないinputのplaceholderで探す
getByTestId 専用のテスト用属性(data-testid)で探す

テストの実行コマンド

# テストを実行
npx playwright test

# ブラウザを表示しながら実行(デバッグ用)
npx playwright test --headed

# UIモードで実行(テスト結果を視覚的に確認)
npx playwright test --ui
← トップページに戻る