T-CREATOR

Playwright とは?導入メリット・他ツールとの差分を要点で理解【初心者向け】

Playwright とは?導入メリット・他ツールとの差分を要点で理解【初心者向け】

Web アプリケーションの品質を保つために、E2E(End-to-End)テストは欠かせません。 しかし、テスト自動化ツールにはさまざまな選択肢があり、どれを選ぶべきか迷っている方も多いのではないでしょうか。

本記事では、近年注目を集めている Playwright について、その特徴や導入メリット、そして他の主要なテストツール(Selenium、Cypress、Puppeteer)との違いを初心者にもわかりやすく解説します。 この記事を読めば、あなたのプロジェクトに最適なテストツールを選ぶための判断材料が得られるでしょう。

背景

E2E テスト自動化の重要性

Web アプリケーション開発において、ユーザーの実際の操作をシミュレートする E2E テストは、リリース前の品質保証に不可欠です。

手動でのテストには限界があり、以下のような課題が存在します。

  • 時間とコストの増大:複雑な画面遷移や操作フローを毎回手動でテストするには膨大な工数が必要
  • 人的ミスのリスク:繰り返しのテスト作業では見落としや確認漏れが発生しやすい
  • リグレッション対応の困難さ:新機能追加時に既存機能への影響を確認するのが難しい

これらの課題を解決するため、E2E テストの自動化が広く行われるようになりました。

E2E テスト自動化ツールの変遷

E2E テスト自動化の歴史を振り返ると、以下のような変遷があります。

#年代ツール特徴
12004 年〜Selenium業界標準として長年使用され、幅広いブラウザ・言語をサポート
22017 年〜PuppeteerGoogle が開発した Chrome/Chromium 特化型のテストツール
32017 年〜CypressJavaScript に特化し、開発者体験を重視した設計
42020 年〜PlaywrightMicrosoft が開発したモダンな自動化ツール

下図は、これらのツールが登場した時系列と主な開発元を示しています。

mermaidflowchart LR
    selenium["Selenium<br/>(2004年〜)"] --> puppeteer["Puppeteer<br/>(2017年〜)"]
    selenium --> cypress["Cypress<br/>(2017年〜)"]
    puppeteer --> playwright["Playwright<br/>(2020年〜)"]

    selenium_org["業界標準<br/>オープンソース"] -.->|開発| selenium
    google["Google"] -.->|開発| puppeteer
    cypress_org["Cypress.io"] -.->|開発| cypress
    microsoft["Microsoft"] -.->|開発| playwright

図で理解できる要点:

  • Selenium は 2004 年から業界標準として使われてきた歴史あるツール
  • 2017 年以降、モダンなアプローチを持つツールが次々と登場
  • Playwright は最も新しく、過去のツールの課題を解決する形で設計されている

Playwright の誕生背景

Playwright は、Microsoft によって 2020 年に公開されました。

実は、Playwright の開発チームは元々 Puppeteer を開発していたメンバーが中心となっています。 彼らは Puppeteer での経験を活かし、さらに進化したテストツールを目指して Playwright を開発しました。

主な開発動機は以下の通りです。

  • クロスブラウザサポートの強化:Chromium だけでなく、WebKit(Safari)、Firefox にも対応
  • より信頼性の高いテスト:flaky test(不安定なテスト)を減らす仕組みの実装
  • モダンな Web アプリへの対応:SPA(Single Page Application)や複雑なインタラクションへの最適化

現在では、GitHub で 61,000 以上のスターを獲得し、週に 400 万回以上 NPM でダウンロードされる人気ツールとなっています。

課題

既存ツールが抱える課題

E2E テストの自動化ツールを選定・導入する際、以下のような課題に直面することがあります。

課題 1:Flaky Test(不安定なテスト)の発生

テストが時々失敗したり成功したりする「Flaky Test」は、テスト自動化における最大の悩みの一つです。

原因としては以下が挙げられます。

  • 明示的な待機処理の必要性:要素の読み込みを待つために sleepwait を手動で記述する必要がある
  • タイミングの問題:ネットワークの遅延やレンダリングの遅れに対応できない
  • 非同期処理の扱いづらさ:JavaScript の非同期処理と同期を取るのが難しい

下図は、従来のテストツールでよく発生する Flaky Test のメカニズムを示しています。

mermaidsequenceDiagram
    participant test as テストコード
    participant browser as ブラウザ
    participant element as DOM要素

    test->>browser: ページ読み込み
    test->>element: 要素をクリック
    Note over element: まだ要素が<br/>読み込まれていない
    element-->>test: エラー:要素が見つからない
    Note over test: テスト失敗<br/>(Flaky Test)

図で理解できる要点:

  • テストコードが要素の準備を待たずに操作を実行してしまう
  • 要素が読み込まれる前にアクションを起こすとテストが失敗する
  • このタイミング問題が Flaky Test の主な原因

課題 2:クロスブラウザテストの複雑さ

実際のユーザーは様々なブラウザを使用するため、複数ブラウザでの動作確認が必要です。

しかし、従来のツールでは以下のような課題があります。

  • ツールごとのブラウザサポート範囲が限定的:Puppeteer は Chrome のみ、Cypress は一部ブラウザのみ
  • ブラウザごとに異なる設定や API:ブラウザごとに別々のコードを書く必要がある
  • WebDriver の設定の煩雑さ:Selenium では各ブラウザの WebDriver を個別にセットアップする必要がある

課題 3:開発体験とメンテナンス性

テストコードも通常のコードと同様に、読みやすさとメンテナンス性が重要です。

多くのツールで以下の課題があります。

  • 冗長なコード記述:明示的な待機処理やエラーハンドリングで冗長になりがち
  • デバッグの困難さ:テストが失敗した際に原因を特定するのが難しい
  • 実行速度の遅さ:大規模なテストスイートでは実行に時間がかかる

課題 4:モダンな Web アプリへの対応

現代の Web アプリケーションは複雑化しており、以下のような機能への対応が求められます。

  • SPA(Single Page Application):動的なコンテンツ更新への対応
  • マルチタブ・マルチウィンドウ:複数のタブやウィンドウを跨ぐ操作
  • 異なるオリジン間の操作:iframe や別ドメインへの遷移
  • 認証・認可のテスト:ログイン状態の保持や権限による画面の出し分け

下図は、モダンな Web アプリでテストツールが対応すべき複雑なシナリオを示しています。

mermaidflowchart TD
    app["Webアプリケーション"] --> spa["SPA<br/>(動的コンテンツ)"]
    app --> multi["マルチタブ<br/>マルチウィンドウ"]
    app --> origin["クロスオリジン<br/>(iframe等)"]
    app --> auth["認証・認可"]

    spa --> challenge1["要素の動的な<br/>追加・削除"]
    multi --> challenge2["複数コンテキストの<br/>管理"]
    origin --> challenge3["セキュリティ制約<br/>への対応"]
    auth --> challenge4["セッション管理<br/>権限制御"]

図で理解できる要点:

  • モダンな Web アプリは 4 つの主要な複雑性を持つ
  • それぞれの領域で異なる技術的課題が存在する
  • テストツールにはこれらすべてに対応できる柔軟性が求められる

テストツール選定の難しさ

これらの課題を踏まえると、テストツールの選定には以下のポイントを考慮する必要があります。

#選定ポイント確認事項
1ブラウザサポート対応ブラウザの範囲と API の統一性
2信頼性Flaky Test の発生しにくさ
3開発体験コードの書きやすさとデバッグのしやすさ
4実行速度テストスイート全体の実行時間
5コミュニティドキュメントの充実度とエコシステムの成熟度

次章では、Playwright がこれらの課題をどのように解決しているのかを見ていきましょう。

解決策

Playwright が提供する解決策

Playwright は、前章で挙げた課題に対して包括的な解決策を提供します。 ここでは、Playwright の主要な機能と、それがどのように課題を解決するのかを詳しく見ていきます。

解決策 1:自動待機機能による信頼性の向上

Playwright の最大の特徴の一つが、**自動待機機能(Auto-waiting)**です。

すべての操作の前に、Playwright は以下を自動的に確認します。

  • 要素が存在するか:DOM に要素が存在することを確認
  • 要素が表示されているかdisplay: nonevisibility: hidden でないことを確認
  • 要素が有効かdisabled 属性がないことを確認
  • 要素が安定しているか:アニメーションが完了していることを確認
  • 要素がクリック可能か:他の要素に覆われていないことを確認

この機能により、開発者は明示的な待機処理を書く必要がありません。

以下は、従来のツールと Playwright の比較です。

従来のツール(Selenium の例)

javascript// 要素が表示されるまで明示的に待つ必要がある
await driver.wait(
  until.elementLocated(By.id('submit-button')),
  5000
);
const button = await driver.findElement(
  By.id('submit-button')
);
await driver.wait(until.elementIsVisible(button), 5000);
await button.click();

上記のコードでは、要素の出現と表示状態を明示的に待つ必要があります。 待機時間(5000 ミリ秒)も開発者が指定しなければなりません。

Playwright の場合

javascript// 自動的に要素の準備を待ってからクリックする
await page.click('#submit-button');

Playwright では、click() メソッドを呼ぶだけで、内部で自動的に要素の準備を待ちます。 コードが非常にシンプルになり、Flaky Test の発生を大幅に減らせるのです。

下図は、Playwright の自動待機機能の動作フローを示しています。

mermaidsequenceDiagram
    participant test as テストコード
    participant pw as Playwright
    participant browser as ブラウザ
    participant element as DOM要素

    test->>pw: page.click('#button')
    pw->>browser: 要素を検索
    browser->>element: 要素の状態確認

    alt 要素が準備できていない
        pw->>pw: 自動的に待機
        pw->>element: 再度状態確認
    end

    element-->>pw: 要素の準備完了
    pw->>element: クリック実行
    element-->>test: 成功

図で理解できる要点:

  • Playwright が要素の状態を自動的にチェックする
  • 準備ができていない場合は自動的に待機して再確認
  • 開発者は待機処理を書く必要がない

解決策 2:統一された API でのクロスブラウザサポート

Playwright は、ChromiumFirefoxWebKit(Safari)の 3 つの主要ブラウザエンジンをサポートしています。

重要なのは、すべてのブラウザに対して 同じ API を使用できることです。

ブラウザの切り替え例

javascript// Chromium でテストを実行
const { chromium } = require('playwright');
const browser = await chromium.launch();

上記のコードで Chromium ブラウザを起動します。 chromium.launch() により、ブラウザインスタンスが作成されます。

javascript// Firefox でテストを実行(API は同じ)
const { firefox } = require('playwright');
const browser = await firefox.launch();

Firefox に切り替える場合も、import 部分を変更するだけで、その後の API は完全に同じです。

javascript// WebKit(Safari)でテストを実行(API は同じ)
const { webkit } = require('playwright');
const browser = await webkit.launch();

WebKit でも同様に、API を変更する必要はありません。

ページ操作の例(すべてのブラウザで共通)

javascript// どのブラウザでも同じコードで動作する
const page = await browser.newPage();
await page.goto('https://example.com');
await page.fill('input[name="email"]', 'test@example.com');
await page.click('button[type="submit"]');

この API の統一性により、クロスブラウザテストを簡単に実装できます。

下図は、Playwright のクロスブラウザサポートの仕組みを示しています。

mermaidflowchart TD
    code["Playwright API<br/>(統一されたインターフェース)"] --> chromium_api["Chromium API"]
    code --> firefox_api["Firefox API"]
    code --> webkit_api["WebKit API"]

    chromium_api --> chromium_browser["Chromium<br/>ブラウザ"]
    firefox_api --> firefox_browser["Firefox<br/>ブラウザ"]
    webkit_api --> webkit_browser["WebKit<br/>(Safari)"]

図で理解できる要点:

  • 開発者は統一された Playwright API のみを使用する
  • Playwright が各ブラウザの固有 API を内部で適切に処理する
  • 同じテストコードで複数ブラウザをテストできる

解決策 3:Web-First Assertions による堅牢なテスト

Playwright は、Web-First Assertions という仕組みを提供しています。

これは、条件が満たされるまで自動的に再試行するアサーション(検証)機能です。

従来のアサーション

javascript// 従来のアサーション(一度だけチェック)
const text = await page.textContent('.status');
expect(text).toBe('Success');

このコードでは、textContent() を呼び出した瞬間の値のみをチェックします。 要素のテキストが非同期で更新される場合、タイミングによっては失敗する可能性があります。

Playwright の Web-First Assertions

javascript// 条件が満たされるまで自動的に再試行
await expect(page.locator('.status')).toHaveText('Success');

このコードでは、.status 要素のテキストが 'Success' になるまで、Playwright が自動的に再試行します。 デフォルトでは最大 5 秒間(カスタマイズ可能)待機します。

複雑な条件の検証例

javascript// 要素が表示されることを検証
await expect(page.locator('.notification')).toBeVisible();

要素が表示されるまで自動的に待機します。

javascript// 要素が特定の属性値を持つことを検証
await expect(page.locator('button')).toHaveAttribute(
  'disabled',
  ''
);

ボタンが disabled 属性を持つまで待機します。

javascript// 要素の数を検証
await expect(page.locator('.item')).toHaveCount(5);

.item クラスを持つ要素が 5 個になるまで待機します。

この機能により、動的に変化する UI に対しても安定したテストを書くことができます。

解決策 4:並列実行による高速化

Playwright は、並列実行機能を標準で提供しています。

テストファイルや個別のテストケースを並列で実行することで、大幅な時間短縮が可能です。

並列実行の設定例

javascript// playwright.config.js
module.exports = {
  // ワーカー数を指定(CPU コア数に応じて自動調整も可能)
  workers: 4,
  // すべてのテストを並列実行
  fullyParallel: true,
};

workers オプションで並列実行するワーカー数を指定します。 fullyParalleltrue にすると、すべてのテストが並列で実行されます。

実行速度の比較データ

各テストツールの平均実行時間を比較すると、以下のようになります。

#ツール名平均実行時間相対速度
1Playwright4.513 秒★★★★★ 最速
2Selenium4.590 秒★★★★☆
3Puppeteer4.784 秒★★★☆☆
4Cypress9.378 秒★★☆☆☆ 最遅

Playwright は最も高速で、Cypress の約 2 倍の速度でテストを実行できます。

解決策 5:モダンな Web アプリへの対応

Playwright は、現代の複雑な Web アプリケーションに必要な機能を標準で提供しています。

マルチタブ・マルチウィンドウのサポート

javascript// 新しいタブを開く
const newPage = await context.newPage();
await newPage.goto('https://example.com/new-tab');

新しいページ(タブ)を簡単に作成できます。

javascript// ポップアップウィンドウを処理
const [popup] = await Promise.all([
  page.waitForEvent('popup'),
  page.click('a[target="_blank"]'),
]);
await popup.waitForLoadState();

ポップアップウィンドウのイベントを待ち受けて処理します。 Promise.all を使うことで、クリックとポップアップの待機を同時に行えます。

認証状態の保存と再利用

javascript// ログイン処理を実行
await page.goto('https://example.com/login');
await page.fill('input[name="username"]', 'user');
await page.fill('input[name="password"]', 'pass');
await page.click('button[type="submit"]');

最初にログイン処理を実行します。

javascript// 認証状態を保存
await context.storageState({
  path: 'auth.json',
});

ログイン後の状態(Cookie やローカルストレージ)をファイルに保存します。

javascript// 保存した認証状態を読み込んで再利用
const context = await browser.newContext({
  storageState: 'auth.json',
});

保存した認証状態を読み込むことで、毎回ログインする必要がなくなります。

この機能により、テスト実行時間を大幅に短縮できます。

iframe やクロスオリジンのサポート

javascript// iframe 内の要素を操作
const frame = page.frame({ name: 'my-iframe' });
await frame.click('button');

iframe を取得して、その中の要素に対して操作を行えます。

javascript// 別オリジンの iframe でも同様に操作可能
const crossOriginFrame = page.frame({
  url: /example\.com/,
});
await crossOriginFrame.fill('input', 'value');

別ドメインの iframe でも同じように操作できます。

下図は、Playwright が対応するモダンな Web アプリの機能を示しています。

mermaidflowchart TD
    playwright["Playwright"] --> feature1["マルチタブ<br/>マルチウィンドウ"]
    playwright --> feature2["認証状態の<br/>保存・再利用"]
    playwright --> feature3["iframe対応<br/>クロスオリジン"]
    playwright --> feature4["ネットワーク<br/>インターセプト"]

    feature1 --> use1["複数ページの<br/>同時操作"]
    feature2 --> use2["ログイン処理<br/>のスキップ"]
    feature3 --> use3["複雑な構造の<br/>ページテスト"]
    feature4 --> use4["API レスポンスの<br/>モック化"]

図で理解できる要点:

  • Playwright は 4 つの主要なモダン機能をサポート
  • それぞれの機能が特定のユースケースに対応
  • これらの機能により、複雑な Web アプリも効率的にテストできる

解決策 6:優れた開発体験

Playwright は、開発者の生産性を高めるための機能を多数提供しています。

Playwright Inspector(デバッグツール)

javascript// デバッグモードで実行(環境変数を設定)
// PWDEBUG=1 を設定するとインスペクターが起動する

環境変数 PWDEBUG=1 を設定してテストを実行すると、Playwright Inspector が起動します。

Playwright Inspector では以下のことができます。

  • テストの実行を一時停止して、ステップバイステップで進められる
  • 各ステップでのブラウザの状態を確認できる
  • CSS セレクターをインタラクティブに試せる
  • スクリーンショットやビデオを確認できる

Codegen(コード生成ツール)

Playwright には、ブラウザ操作を記録して自動的にテストコードを生成する機能があります。

bash# コード生成モードで Playwright を起動
yarn playwright codegen https://example.com

このコマンドを実行すると、ブラウザと Playwright Inspector が起動します。

実際にブラウザを操作すると、Playwright が自動的にコードを生成してくれます。

  • ページ遷移、クリック、入力などの操作が自動的にコードに変換される
  • 生成されたコードはそのままテストに使用できる
  • セレクターの書き方も学べる

初心者がテストコードの書き方を学ぶのに最適なツールです。

トレースビューアー(実行履歴の可視化)

javascript// トレースを記録する設定
await context.tracing.start({
  screenshots: true,
  snapshots: true,
});

トレースの記録を開始します。 スクリーンショットと DOM のスナップショットを保存します。

javascript// テストを実行
await page.goto('https://example.com');
await page.click('button');

テストの各ステップが記録されます。

javascript// トレースを保存
await context.tracing.stop({
  path: 'trace.zip',
});

記録したトレースをファイルに保存します。

保存されたトレースファイルは、Playwright のトレースビューアーで開くことができます。

トレースビューアーでは以下が確認できます。

  • 各操作のタイムライン
  • スクリーンショットと DOM の状態
  • ネットワークリクエストとレスポンス
  • コンソールログ

テストが失敗した際の原因特定が非常に簡単になります。

Playwright の導入メリットまとめ

これまで見てきた解決策をまとめると、Playwright には以下のメリットがあります。

#メリット詳細
1高い信頼性自動待機機能により Flaky Test を大幅に削減
2統一された API同じコードで複数ブラウザをテスト可能
3高速な実行並列実行により Cypress の約 2 倍の速度を実現
4モダンな機能マルチタブ、認証状態の保存、iframe 対応など
5優れた DXデバッグツール、コード生成、トレースビューアーなど
6多言語サポートTypeScript, JavaScript, Python, .NET, Java に対応

次章では、具体的なコード例を通じて、Playwright の使い方を見ていきます。

具体例

Playwright の基本的な使い方

ここでは、実際に Playwright を使ったテストコードの例を段階的に見ていきます。 初心者でも理解しやすいよう、各ステップを詳しく解説します。

環境構築

まず、Playwright をプロジェクトにインストールします。

Playwright のインストール

bash# Playwright を開発用の依存関係としてインストール
yarn add -D @playwright/test

このコマンドで、Playwright のテストランナーとブラウザドライバーがインストールされます。

bash# ブラウザをインストール
yarn playwright install

Chromium、Firefox、WebKit の 3 つのブラウザがダウンロードされます。

プロジェクトの初期化

bash# Playwright の設定ファイルを生成
yarn playwright init

このコマンドで、playwright.config.js が生成され、基本的な設定が行われます。

基本的なテストコードの作成

実際のテストコードを見ていきましょう。 ここでは、シンプルな検索機能のテストを例にします。

テストファイルの作成(tests/search.spec.js)

javascript// Playwright のテスト機能をインポート
const { test, expect } = require('@playwright/test');

test はテストケースを定義する関数、expect はアサーションを行う関数です。

基本的なページ遷移のテスト

javascript// テストケースを定義
test('検索ページにアクセスできる', async ({ page }) => {
  // ページに移動
  await page.goto('https://example.com');

  // タイトルが正しいことを確認
  await expect(page).toHaveTitle(/Example Domain/);
});

test() 関数の第 1 引数がテストの説明、第 2 引数が実際のテストコードです。 page オブジェクトが自動的に渡され、ブラウザ操作ができます。

フォーム入力とクリックのテスト

javascripttest('検索キーワードを入力して検索できる', async ({
  page,
}) => {
  // 検索ページに移動
  await page.goto('https://example.com/search');

  // 検索ボックスにキーワードを入力
  await page.fill('input[name="q"]', 'Playwright');

  // 検索ボタンをクリック
  await page.click('button[type="submit"]');

  // 検索結果ページに遷移したことを確認
  await expect(page).toHaveURL(/search\?q=Playwright/);
});

fill() でフォーム入力、click() でボタンクリックを行います。 すべての操作で自動待機が行われます。

検索結果の検証

javascripttest('検索結果が表示される', async ({ page }) => {
  // 検索を実行(前のステップと同じ)
  await page.goto('https://example.com/search');
  await page.fill('input[name="q"]', 'Playwright');
  await page.click('button[type="submit"]');

  // 検索結果が表示されることを確認
  await expect(
    page.locator('.search-result')
  ).toBeVisible();

  // 検索結果が 1 件以上あることを確認
  await expect(page.locator('.search-result')).toHaveCount(
    (count) => count > 0
  );
});

locator() で要素を取得し、toBeVisible() で表示されているかを確認します。 toHaveCount() で要素の数を検証できます。

より実践的なテストパターン

実際のプロジェクトでよく使用されるパターンを見ていきましょう。

ログイン機能のテスト

javascript// ログインのテストグループを定義
test.describe('ログイン機能', () => {
  // 各テストの前に実行される処理
  test.beforeEach(async ({ page }) => {
    // ログインページに移動
    await page.goto('https://example.com/login');
  });

test.describe() で関連するテストをグループ化します。 test.beforeEach() で各テストの前処理を定義します。

javascripttest('正しい認証情報でログインできる', async ({ page }) => {
  // メールアドレスを入力
  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/);

  // ユーザー名が表示されることを確認
  await expect(page.locator('.user-name')).toHaveText(
    'user@example.com'
  );
});

ログイン成功のシナリオをテストします。

javascript  test('誤った認証情報でエラーが表示される', async ({ page }) => {
    // 誤ったパスワードを入力
    await page.fill('input[name="email"]', 'user@example.com');
    await page.fill('input[name="password"]', 'wrongpassword');
    await page.click('button[type="submit"]');

    // エラーメッセージが表示されることを確認
    await expect(page.locator('.error-message'))
      .toBeVisible();

    // エラーメッセージの内容を確認
    await expect(page.locator('.error-message'))
      .toHaveText(/認証に失敗しました/);
  });
});

ログイン失敗のシナリオもテストします。

ネットワークリクエストのモック化

javascripttest('API レスポンスをモック化する', async ({ page }) => {
  // API リクエストをインターセプト
  await page.route('**/api/users', (route) => {
    // モックデータを返す
    route.fulfill({
      status: 200,
      contentType: 'application/json',
      body: JSON.stringify([
        { id: 1, name: 'テストユーザー1' },
        { id: 2, name: 'テストユーザー2' }
      ])
    });
  });

page.route() で特定の URL へのリクエストをインターセプトします。 route.fulfill() でモックレスポンスを返します。

javascript  // ページに移動(モックされた API が呼ばれる)
  await page.goto('https://example.com/users');

  // モックデータが表示されることを確認
  await expect(page.locator('.user-item'))
    .toHaveCount(2);
  await expect(page.locator('.user-item').first())
    .toHaveText('テストユーザー1');
});

モックされたデータが正しく表示されるかを確認します。

スクリーンショットとビデオの記録

javascripttest('失敗時のスクリーンショットを保存', async ({
  page,
}) => {
  await page.goto('https://example.com');

  try {
    // 存在しない要素をクリック(失敗する)
    await page.click('.non-existent-button');
  } catch (error) {
    // エラー時にスクリーンショットを撮影
    await page.screenshot({
      path: 'screenshots/error.png',
      fullPage: true,
    });
    throw error;
  }
});

screenshot() メソッドで画面全体のスクリーンショットを保存します。

テストが失敗した際のデバッグに役立ちます。

設定ファイルのカスタマイズ

実際のプロジェクトでは、設定ファイルをカスタマイズすることが重要です。

playwright.config.js の設定例

javascript// Playwright の設定をエクスポート
module.exports = {
  // テストファイルのパスパターン
  testDir: './tests',

  // タイムアウト設定(ミリ秒)
  timeout: 30000,

testDir でテストファイルの配置場所を指定します。 timeout でテスト全体のタイムアウトを設定します。

javascript  // 並列実行の設定
  workers: process.env.CI ? 2 : 4,
  fullyParallel: true,

  // リトライ設定(CI 環境でのみリトライ)
  retries: process.env.CI ? 2 : 0,

workers で並列実行数を設定します。CI 環境では少なめに設定すると安定します。 retries で失敗時のリトライ回数を指定します。

javascript  // レポーター設定
  reporter: [
    ['html', { outputFolder: 'playwright-report' }],
    ['json', { outputFile: 'test-results.json' }]
  ],

reporter でテスト結果のレポート形式を指定します。 HTML レポートと JSON レポートを同時に生成する例です。

javascript  // 使用するブラウザの設定
  projects: [
    {
      name: 'chromium',
      use: { browserName: 'chromium' }
    },
    {
      name: 'firefox',
      use: { browserName: 'firefox' }
    },
    {
      name: 'webkit',
      use: { browserName: 'webkit' }
    }
  ]
};

projects で複数のブラウザを設定します。 1 回のテスト実行で 3 つのブラウザすべてでテストされます。

下図は、Playwright のテスト実行フローを示しています。

mermaidflowchart TD
    start["テスト実行開始"] --> config["設定ファイル読み込み"]
    config --> parallel["並列実行設定"]

    parallel --> chrome["Chromium<br/>ワーカー"]
    parallel --> firefox["Firefox<br/>ワーカー"]
    parallel --> webkit["WebKit<br/>ワーカー"]

    chrome --> test1["テストスイート実行"]
    firefox --> test2["テストスイート実行"]
    webkit --> test3["テストスイート実行"]

    test1 --> report["レポート生成"]
    test2 --> report
    test3 --> report

    report --> done["テスト完了"]

図で理解できる要点:

  • 設定ファイルに基づいて複数のブラウザで並列実行される
  • 各ブラウザでテストスイートが独立して実行される
  • すべての結果が統合されてレポートが生成される

テストの実行方法

作成したテストを実行する方法を見ていきましょう。

すべてのテストを実行

bash# すべてのテストを実行
yarn playwright test

設定されたすべてのブラウザでテストが実行されます。

特定のブラウザでのみ実行

bash# Chromium のみでテストを実行
yarn playwright test --project=chromium

--project オプションで特定のブラウザを指定できます。

特定のファイルのみ実行

bash# 特定のテストファイルのみ実行
yarn playwright test tests/search.spec.js

ファイルパスを指定すると、そのファイルのテストのみが実行されます。

UI モードで実行

bash# UI モードでテストを実行
yarn playwright test --ui

--ui オプションで GUI でテストを実行・管理できます。 各テストの実行状況をビジュアルに確認できます。

デバッグモードで実行

bash# デバッグモードでテストを実行
yarn playwright test --debug

--debug オプションで Playwright Inspector が起動し、ステップバイステップで実行できます。

この具体例で、Playwright の基本的な使い方から実践的なパターンまでを網羅しました。 次章では、他のツールとの詳細な比較を見ていきます。

他ツールとの詳細比較

ここでは、Playwright と他の主要なテストツール(Selenium、Cypress、Puppeteer)との違いを、具体的なポイントで比較します。

全体的な比較表

まず、各ツールの特徴を一覧で比較しましょう。

#項目PlaywrightSeleniumCypressPuppeteer
1リリース年2020 年2004 年2017 年2017 年
2開発元MicrosoftOpenSourceCypress.ioGoogle
3対応ブラウザChromium, Firefox, WebKitすべてChrome, Edge, FirefoxChrome, Firefox
4言語サポートJS, TS, Python, .NET, JavaJava, Python, C#, JS 等JS, TS のみJS, TS のみ
5自動待機ありなしあり部分的
6平均実行速度4.513 秒4.590 秒9.378 秒4.784 秒
7マルチタブサポートサポート非サポートサポート
8GitHub スター61,000+28,000+45,000+85,000+
9NPM 週間 DL400 万回-500 万回300 万回

Playwright vs Selenium

Selenium は業界標準として長年使われてきたツールです。

Selenium の強み

java// Selenium の例(Java)
WebDriver driver = new ChromeDriver();
driver.get("https://example.com");
WebElement button = driver.findElement(By.id("submit"));
button.click();

Selenium は Java を始めとする多くの言語で利用できます。 レガシーブラウザ(IE11 など)のサポートも可能です。

Playwright の優位性

javascript// Playwright の例
await page.goto('https://example.com');
await page.click('#submit');

Playwright は同じことをよりシンプルに書けます。 自動待機により、明示的な待機処理が不要です。

主な違い

下図は、Selenium と Playwright のアーキテクチャの違いを示しています。

mermaidflowchart LR
    subgraph selenium["Selenium アーキテクチャ"]
        sel_test["テストコード"] -->|HTTP| webdriver["WebDriver"]
        webdriver -->|ブラウザ<br/>固有プロトコル| sel_browser["ブラウザ"]
    end

    subgraph playwright_arch["Playwright アーキテクチャ"]
        pw_test["テストコード"] -->|WebSocket| pw_protocol["Playwright<br/>Protocol"]
        pw_protocol -->|直接制御| pw_browser["ブラウザ"]
    end

図で理解できる要点:

  • Selenium は HTTP 経由で WebDriver と通信し、ブラウザを間接的に制御する
  • Playwright は WebSocket で直接ブラウザを制御するため高速で安定している
  • アーキテクチャの違いが実行速度と信頼性に影響する

選択の指針

Selenium を選ぶべきケース:

  • レガシーブラウザ(IE11 など)のサポートが必要
  • Java や C# などの言語で既存のテストコードがある
  • 長年培われたエコシステムと豊富な情報が必要

Playwright を選ぶべきケース:

  • モダンなブラウザのみをサポートすればよい
  • 高速で安定したテストを実現したい
  • JavaScript/TypeScript で開発している

Playwright vs Cypress

Cypress は開発者体験を重視した人気のツールです。

Cypress の特徴

javascript// Cypress の例
cy.visit('https://example.com');
cy.get('#submit').click();
cy.url().should('include', '/success');

Cypress は独自の API を持ち、直感的に書けます。 ブラウザ内で実行されるため、デバッグがしやすいという特徴があります。

Playwright との違い

javascript// Playwright の例
await page.goto('https://example.com');
await page.click('#submit');
await expect(page).toHaveURL(/\/success/);

Playwright は標準的な async/await 構文を使います。

主な違いの比較

マルチタブサポートの違い:

javascript// Playwright: マルチタブに対応
const [newPage] = await Promise.all([
  context.waitForEvent('page'),
  page.click('a[target="_blank"]'),
]);
await newPage.waitForLoadState();
await newPage.click('#button-in-new-tab');

Playwright では複数のタブを同時に操作できます。

Cypress ではマルチタブの操作ができないため、この種のテストは困難です。

実行速度の違い

前述の通り、Playwright(4.513 秒)は Cypress(9.378 秒)の約 2 倍の速度です。

これは、大規模なテストスイートでは大きな差となります。

選択の指針

Cypress を選ぶべきケース:

  • JavaScript/TypeScript のみで開発している
  • 単一タブでの操作のみをテストする
  • リアルタイムリロード機能を重視する

Playwright を選ぶべきケース:

  • マルチタブやポップアップのテストが必要
  • 複数ブラウザでのテストが必須
  • 高速な実行速度を求める

Playwright vs Puppeteer

Puppeteer は Playwright の前身とも言えるツールです。

Puppeteer の特徴

javascript// Puppeteer の例
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.goto('https://example.com');
await page.click('#submit');

Puppeteer の API は Playwright と非常に似ています。 実際、Playwright は Puppeteer の開発チームが作成しています。

Playwright の拡張点

ブラウザサポートの違い:

javascript// Playwright: 複数ブラウザをサポート
const { chromium, firefox, webkit } = require('playwright');
const browser = await webkit.launch(); // Safari でテスト

Playwright は WebKit(Safari)もサポートします。

Puppeteer は Chrome/Chromium と Firefox のみです。

自動待機機能の充実度:

javascript// Playwright: より高度な自動待機
await page.click('#submit', {
  // クリック前の状態を細かく指定
  force: false, // 要素が覆われていたら失敗
  noWaitAfter: false, // ナビゲーション待機
  trial: false, // 実際にクリックせず可能性だけチェック
});

Playwright はより細かい制御が可能です。

選択の指針

Puppeteer を選ぶべきケース:

  • Chrome/Chromium のみをサポートすればよい
  • スクレイピングやパフォーマンス計測が主目的
  • 軽量なツールを求める

Playwright を選ぶべきケース:

  • 複数ブラウザでのテストが必要
  • より堅牢な自動待機機能を求める
  • E2E テストを包括的に行いたい

ツール選択のフローチャート

最後に、どのツールを選ぶべきかのフローチャートを示します。

mermaidflowchart TD
    start["E2Eテストツールを選ぶ"] --> q1{"レガシーブラウザ<br/>(IE11等)<br/>サポートが必要?"}

    q1 -->|はい| selenium["Selenium<br/>を選択"]
    q1 -->|いいえ| q2{"Chrome/Chromium<br/>のみでよい?"}

    q2 -->|はい| q3{"スクレイピングや<br/>パフォーマンス<br/>計測が主目的?"}
    q2 -->|いいえ| q4{"マルチタブや<br/>複雑な操作が<br/>必要?"}

    q3 -->|はい| puppeteer["Puppeteer<br/>を選択"]
    q3 -->|いいえ| q4

    q4 -->|はい| playwright["Playwright<br/>を選択"]
    q4 -->|いいえ| q5{"実行速度より<br/>開発体験を<br/>優先する?"}

    q5 -->|はい| cypress["Cypress<br/>を選択"]
    q5 -->|いいえ| playwright

図で理解できる要点:

  • まずレガシーブラウザサポートの必要性を判断
  • 次に対象ブラウザと主な用途を考慮
  • 最後に必要な機能と優先事項で決定
  • 多くのケースで Playwright が最適な選択肢となる

この比較を参考に、プロジェクトの要件に最適なツールを選択してください。

まとめ

本記事では、Playwright の特徴、導入メリット、そして他のテストツールとの違いについて詳しく解説してきました。

最後に、重要なポイントを振り返りましょう。

Playwright の主要な特徴

Playwright は以下の特徴を持つモダンな E2E テスト自動化ツールです。

信頼性の高さ

自動待機機能により、Flaky Test(不安定なテスト)を大幅に削減できます。 明示的な待機処理を書く必要がなく、コードがシンプルになります。

クロスブラウザサポート

Chromium、Firefox、WebKit の 3 つの主要ブラウザエンジンに対応しています。 すべてのブラウザで同じ API を使用できるため、ブラウザごとにコードを書き分ける必要がありません。

高速な実行

並列実行機能により、他のツールと比較して最も高速なテスト実行を実現します。 Cypress と比較すると約 2 倍の速度で、大規模なテストスイートでも効率的に実行できます。

モダンな機能

マルチタブ・マルチウィンドウのサポート、認証状態の保存と再利用、iframe やクロスオリジンへの対応など、現代の複雑な Web アプリケーションに必要な機能を標準で提供しています。

優れた開発体験

Playwright Inspector によるデバッグ、Codegen によるコード生成、トレースビューアーによる実行履歴の可視化など、開発者の生産性を高める機能が充実しています。

他ツールとの使い分け

各ツールにはそれぞれの強みがあります。 プロジェクトの要件に応じて最適なツールを選択しましょう。

Playwright が最適なケース

  • モダンなブラウザのみをサポートすればよい
  • クロスブラウザテストが必須である
  • マルチタブや複雑な操作のテストが必要
  • 高速で安定したテストを実現したい
  • TypeScript/JavaScript で開発している

他のツールを選ぶべきケース

Selenium を選ぶ:レガシーブラウザのサポートが必要、Java や C# での開発が中心

Cypress を選ぶ:単一タブでの操作のみ、リアルタイムリロード機能を重視

Puppeteer を選ぶ:Chrome のみで十分、スクレイピングやパフォーマンス計測が主目的

Playwright 導入の次のステップ

Playwright を導入する際は、以下のステップで進めるとよいでしょう。

ステップ 1:小規模な試験導入

まずは重要度の高い機能に対して、少数のテストケースを作成してみましょう。 Playwright の基本的な使い方を習得し、チームでの知見を蓄積します。

ステップ 2:CI/CD への統合

GitHub Actions や GitLab CI などの CI/CD パイプラインに Playwright を組み込みます。 Pull Request のたびに自動的にテストが実行される環境を構築しましょう。

ステップ 3:テストカバレッジの拡大

段階的にテストケースを増やし、カバレッジを向上させます。 並列実行機能を活用して、実行時間の増加を抑えながら拡大できます。

ステップ 4:継続的な改善

テストの実行結果を分析し、Flaky Test や遅いテストを特定して改善します。 トレースビューアーやレポート機能を活用して、継続的に品質を向上させましょう。

最後に

E2E テストの自動化は、Web アプリケーションの品質を保つために欠かせません。

Playwright は、過去のツールの課題を解決し、モダンな Web アプリケーションに最適化された強力なツールです。 特に、自動待機機能による信頼性の高さと、クロスブラウザサポートの充実は大きな魅力でしょう。

本記事で解説した内容を参考に、ぜひ Playwright の導入を検討してみてください。 初めて E2E テストを書く方でも、Codegen や Playwright Inspector といったツールを活用すれば、スムーズに始められます。

あなたのプロジェクトに最適なテストツールを選択し、高品質な Web アプリケーションの開発を実現しましょう。

関連リンク