PlaywrightではじめるE2Eテスト入門(前編)

フロントエンド開発では、UIの動作を含めてアプリ全体を検証するE2E(End-to-End)テストの重要性が高まっています。一方で、E2Eテストは導入や運用のハードルが高いと感じられがちです。本記事では、Playwrightを使って無理なくE2Eテストを始める方法を解説します。

PlaywrightプレイライトはMicrosoftが開発するE2Eテストツールです。テストはTypeScript(およびJavaScript)で記述できるため、ウェブ開発で馴染みのある言語の知識をそのまま活かせます。

主要なブラウザを単一のAPIでテストできる点や、テストを高速に実行できる点が評価されています。私たちICSでも、Playwrightの導入経験があります。既存機能に改訂の影響が出ていないことを確認する、いわゆるリグレッションテストの目的で活用しています。

本記事では、前後編に分けて、シンプルなウェブアプリを題材に、Playwrightを使ったE2Eテストの流れを解説します。ウェブ開発を担当するフロントエンドエンジニアの方は、ぜひご一読ください。

前編である今回は、Playwightの導入からテストの書き方、便利な支援ツールについて解説します。

本記事の対象読者

  • E2Eテストの導入を試してみたい方
  • E2Eテストの必要性は感じているものの、まだ導入できていない方
  • Playwrightについて基本を知りたい方

本記事のゴール

  • Playwrightで「何ができるか」を短時間で把握する
  • テストの基本、コード生成についてステップを追って理解する

E2Eテストとは

E2Eテストとは、ユーザーの実際の操作と同じ流れで、アプリケーション全体の動作を確認するテスト手法です。ボタンのクリックやフォームへの入力、画面の遷移といった操作をブラウザ上で自動的に再現し、画面が正しく反応するかを検証します。

ユニットテストが個々の関数やロジックを検証するのに対し、E2Eテストでは画面表示やUIの挙動を含めた一連のユーザー体験を検証できる点が特徴です。ユニットテストでは保証できない、次のようなユーザー視点の挙動を確認するのに適しています

  • ボタンを押すとモーダルダイアログが表示される
  • 入力した内容が画面に反映される
  • 画面が遷移する
  • エラー時にエラーメッセージが表示される

ユニットテストについては、記事『JavaScriptのユニットテストを始めよう - ユニットテストのメリットと書き方のコツ』で解説しています。

Playwrightの特徴

Playwrightは、低コストでE2Eテスト環境を導入できるテストツールです。ブラウザ操作やレポート出力など、実務に必要な機能を標準で備えています。

マルチブラウザ対応

Chromium / Firefox / WebKitをサポートしており(ChromeやEdgeはChromiumとして、Safari相当はWebKitとしてテスト可能)、同じテストを複数ブラウザで実行できます。

自動待機

要素の表示や操作可能状態を自動で待つため、「待ち時間の調整」に悩まされにくいのが特徴です。明示的に待機コードを書かなくても安定してテストが動作します。

UI操作の高い再現性

ユーザー操作(クリック、スクロール、入力など)を実際のブラウザ操作に近い形で再現できます。

高速な動作

Playwrightはブラウザを直接効率よく制御し、標準で並列実行できるため、E2Eテスト全体の実行時間を短縮できます。また、前述の自動待機により不要な固定待機やリトライが減り、安定性と速度の両立につながっています。

強力な補助機能

  • ネットワークモック
  • デバイスエミュレーション
  • テストコード自動生成
  • 実行トレースの可視化(後編の記事で解説)

他の代表的なE2EフレームワークであるCypressSeleniumと比べても、安定性、速度、開発体験のバランスが良いのがPlaywrightの強みです。

Playwrightのテスト実施を体験する

この記事で紹介するテストコードのプロジェクトは以下から確認できます。実際に手元で動かせるので試してみてください。

まずは、Playwrightのテストがどのように行われるか体験してみましょう。上記のリポジトリをクローンし、依存パッケージをインストールした上で、次のコマンドを実行します。

# 依存パッケージをインストール
npm install
# PlaywrightのテストランナーをUIモードで起動
npm run e2eTest_ui

PlaywrightのテストランナーがUIモードで立ち上がり、ここからテストを実行できるようになります。実行ボタンを押すとテストが開始します。

UIテストランナー

テストが完了すると、成功したテストは緑色、失敗したテストは赤色で結果表示されます。各テストを選択すると、詳細な実行ログやスクリーンショットを確認できます。

たとえば、上部の時系列を選択すると、テストの各ステップで撮影されたスクリーンショットが表示されます。どの操作で失敗したかを視覚的に把握できます。

UIテストランナーによるテスト結果

UIモードのテストランナーをいろいろ操作してみると、PlaywrightでどのようにE2Eテストが実行されるかのイメージがつかめると思います。これをふまえて、実際にPlaywrightを導入し、テストコードを書いていく流れを解説します。

Playwrightの導入

既存プロジェクトにPlaywrightを導入するステップを説明します。本記事では、Vite + TypeScriptのプロジェクトを例に説明します(既存でも新規でも手順は同じです)。

0. プロジェクトの準備

既存プロジェクトの代替として、先にVite + TypeScriptのプロジェクトを作成しておきます。

Viteの構成について、詳しくは記事『jQueryからTypeScript・Reactまで! - Viteで始めるモダンで高速な開発環境構築』で解説しています。

▼Viteで新規プロジェクトを作成

npm init vite@latest
# frameworkは「vanilla」を、variantは「TypeScript」を選択

1. パッケージのインストール

プロジェクトにテストランナーのパッケージを追加します。また、Playwrightがテストに使用するブラウザのインストールが必要なので追加します。

# テストランナーをプロジェクトに追加
npm install -D @playwright/test
# Playwrightがテストに使用するブラウザをインストール
npx playwright install

2. playwright.config.tsの構成

Playwrightの設定ファイルをプロジェクトのルートに追加します。設定項目はいくつかありますが、よく使うものに絞って説明します。

playwright.config.ts

import {defineConfig, devices} from '@playwright/test';

export default defineConfig({
  // テストの配置ディレクトリを指定
  testDir: './tests',
  // テスト実行時に使用するウェブサーバーを指定
  webServer: {
    command: 'npm run dev -- --port 5180',
    url: 'http://localhost:5180',
  },
  use: {
    // テスト対象の基準URLを指定
    baseURL: 'http://localhost:5180/',
    // ブラウザをヘッドレスで起動するかどうか
    headless: true,
  },
  // テスト実行環境のバリエーションを指定
  projects: [
    {
      name: 'chromium',
      use: {...devices['Desktop Chrome']},
    },
    // 他のブラウザ(省略)
  ],
});

testDir

テストの配置ディレクトリを指定します。ユニットテストなど、他のテスト環境がある場合に別のディレクトリを指定することで共存できます。

webServer

テスト実行時に使用するウェブサーバーを指定します。Viteなどのコマンドをあらかじめ実行しておかなくても、自動でウェブサーバーを立ち上げて実行します。ポートの指定をすることで開発で使用中のポートとの競合を避けられます。

使用するポートを指定したい場合は、webServer.command内(Viteのviteコマンドの場合--portオプション)で指定しましょう。

use.baseURL

テスト対象の基準URLを指定します。ページの移動を行うpage.goto()のルートはこのURLになります。

use.headless

ブラウザをヘッドレスで起動するかどうかを指定します。テストは裏で実行すればよいので、基本的にはtrue(もしくはheadlessの指定自体なし)で構いませんが、動いている様子を確認したい場合などにこの項目の存在を覚えておくとよいでしょう。

projects

対象ブラウザや画面サイズなど、テスト実行の環境を複数指定できます。

3. テストの作成

playwright.config.tsファイルでtestDirとして指定したtestsディレクトリにテストファイルを追加します。app.spec.tsファイルのように.spec.tsで終わるファイルがテストとして認識されます。

▼プロジェクト構造例。☆がPlaywright関連ファイル

project/
├─ public
├─ src
├─ tests/
│  └─ ☆app.spec.ts
├─ index.html
├─ package.json
├─ ☆playwright.config.ts
└─ tsconfig.json

ここでは、ボタンをクリックし、テキストの変化を確認するテストコードを紹介します。要素を取得する際に使用するLocatorについては後述します。

要素を取得し、expect()で評価するのがE2Eテストの基本的な流れです。ユニットテストと似ているので、ユニットテストを書いたことがある人はすんなり理解できるでしょう。

tests/app.spec.ts

import {expect, test} from '@playwright/test';

test('カウンターボタンをクリックすると値が増える', async ({page}) => {
  await page.goto('/');
  // ボタンのLocatorを取得
  const counter = page.getByRole('button', {name: 'count is'});
  // Locatorのテキストを評価
  await expect(counter).toHaveText('count is 0');
  // Locatorの操作
  await counter.click();
  // Locatorのテキストを評価
  await expect(counter).toHaveText('count is 1');
});

4. テストの実行

playwright testコマンドを実行すると、playwright.config.tsファイルに指定したディレクトリのテストが実行されます。

package.jsonのnpm scriptsにコマンドを追加

"scripts": {
  "e2eTest": "playwright test"
}

▼npm scriptsに追加したコマンドを実行

npm run e2eTest

Locator

Playwrightでは、画面上の要素を取得・操作するためにLocator(ロケーター)という仕組みを使います。

従来のE2Eテストでは、要素取得のタイミングが早すぎて存在しない、再描画後に要素が差し替わり参照が壊れる、といった理由でテストが不安定になることがよくありました。

PlaywrightのLocatorには、要素が表示されるまで自動で待つ、DOMが更新されても正しい要素を再取得する、といった自動待機の仕組みがあります。そのため、待機の処理を自分で書かなくても安定したテストが書けるのが特徴です。

Locatorはロールで取得しよう

Playwrightでは、アクセシビリティーのロール情報をもとにしたLocatorの取得方法が推奨されています。

const button = page.getByRole('button', { name: '送信' });

このbuttonは単なるDOMのスナップショットではなく、「条件を満たす要素を探し続ける参照」として扱われます。そのため、画面の再描画や要素の一時的な削除・再生成が発生しても、都度要素を取得し直す必要がなく、安定したテストを実施できます。

getByRole()でLocatorを取得するメリットは次の通りです。

  • 見た目や構造変更に強い・・・getByRole()を使用せず、状態の変化などで頻繁に変わりやすいクラス名で指定すると、参照が途切れてしまうことがある
  • 人間の操作に近い意味で指定できる・・・ユーザーが認識する役割や名前(ボタン、リンク、ラベルなど)で指定できる
  • アクセシビリティーが向上する・・・getByRole()を使うにはアクセシブルなDOMが必要なため、結果としてもアクセシビリティーを意識した実装になる

単なるCSSセレクターよりも意図が読み取りやすいテストになります。

ロールでの取得が難しい場合は、page.locator()メソッドでセレクターを直接指定することもできます。

const input = page.locator('input[name="email"]');

Codegen(コードジェネレーター)

Playwrightには、ブラウザ操作をそのままテストコードとして記録できるコードジェネレーター機能があります。コードジェネレーターを使うと、手動でテストコードを書く手間が大幅に削減され、学習コストも低くなります

今回のアプリでは、devコマンドを実行してサーバーを立ち上げた上で下記のコマンドを実行します。Viteのdevコマンドはデフォルトではポート5173でサーバーが起動するので、コードジェネレーターもポート5173にアクセスするように指定します。

npx playwright codegen http://localhost:5173

playwright codegenコマンドを実行すると、アプリがブラウザで自動的に立ち上がります。この状態でブラウザを操作すると、行った操作がテストコードとして自動的に記録されます。

▼コードジェネレーターを起動し、ブラウザ操作を記録している様子

コードジェネレーターによる操作の記録

生成コードはすべての操作が記録されているため、そのまま使うとテストとして煩雑になります。あくまでテストコードのベースとし、Locatorの取得や行う操作のヒントのように使用するとよいでしょう。

まとめ

E2Eテストは、設定や運用コストが高いという理由から、小規模なアプリでは導入を見送られることも少なくありません。しかし、Playwrightは導入が容易なため、運用コストを抑えながら品質の担保が可能です。ぜひあなたのプロジェクトにもE2Eテストを組み込んでみましょう。

本記事では、Playwrightを使ったE2Eテストの基本的な使い方を紹介しました。後編では、失敗したテストの解析に役立つトレース機能と、GitHub Actionsを使用してCI環境でE2Eテストを実行する方法を紹介します。

SNSでシェアしよう
シェアいただくと、サイト運営の励みになります!
X(旧Twitter)へポスト
はてなブックマークへ投稿
URLをコピー
川勝 研太郎

インタラクティブディベロッパー。ゲーム技術、GPUとその周辺技術について日々勉強中。自宅周辺の移動手段は自転車。

この担当の記事一覧