Playwright導入で躓いた点とその解決策
はじめに
先日、自分が担当しているプロジェクトにPlaywrightを導入しました。
とても便利で使い勝手が良いと感じましたが、導入の際に躓いた点もいくつかありました。
そこで備忘録も兼ねて皆さんに共有したいと思います!💫
前提
- Playwrightを導入したプロジェクトは至ってシンプルなフォーム
- CIでテストを実行する
①実行時間が長すぎる!
シンプルなプロジェクトですが、テストケースは300件以上にもなりました。
これをそのままGitHub Actionsで実行すると、処理に膨大な時間がかかりました。。
(約40分間動きづけていたため、堪らずキャンセルしました笑)
そこでこれを解消するためにShardingを設定しました。
そもそもShardingとは、複数のマシンやプロセスで同時にテストを実行し(並列化)することで、全体の実行時間を短縮する手法です。
Playwrightでは、テストを「シャード」と呼ばれる小さな単位に分割します。
各シャードは独立して実行できるため、並列化が可能です。
今回の例では、テストを4つのシャードに分割し、それぞれを独立したジョブとして実行させます。
最後に、それぞれのシャードから出力されたテスト結果を統合し、最終的なHTMLレポートを生成します。
実装手順
まずは、テスト結果のレポート出力形式をCI時のみblobに変更します。
これはシャーディングによって4つに分割されたシャードで、blobレポートを出力する必要があるためです。
// playwright.config.ts
export default defineConfig({
testDir: './tests',
reporter: process.env.CI ? 'blob' : 'html',
});
あとはgithub actionsのワークフローを作成します。
name: Playwright
# ワークフローを手動でトリガーするためにworkflow_dispatchを指定
on:
workflow_dispatch: {}
jobs:
test:
timeout-minutes: 60
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
shardIndex: [1, 2, 3, 4]
shardTotal: [4]
defaults:
run:
working-directory: application
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
# Node.jsのバージョンをpackage.jsonで指定
node-version-file: application/package.json
cache: "npm"
cache-dependency-path: "**/package-lock.json"
# 依存関係をインストール
- name: Install dependencies
run: npm ci
# Playwright browsersをインストール
- name: Install Playwright Browsers
run: npx playwright install --with-deps
# Playwrightのテストを実行
- name: Run Playwright tests
run: npx playwright test --shard=${{ matrix.shardIndex }}/${{ matrix.shardTotal }}
# Playwrightのテスト結果レポートをアーティファクトとしてアップロード
- uses: actions/upload-artifact@v4
if: ${{ !cancelled() }}
with:
name: blob-report-${{ matrix.shardIndex }}
path: ./application/blob-report
retention-days: 30 # アーティファクトの保存期間を30日に設定
merge-reports:
# Merge reports after playwright-tests, even if some shards have failed
if: ${{ !cancelled() }}
# testが完了した後に実行させる
needs: [test]
runs-on: ubuntu-latest
defaults:
run:
working-directory: application
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
# Node.jsのバージョンをpackage.jsonで指定
node-version-file: application/package.json
cache: "npm"
cache-dependency-path: "**/package-lock.json"
- name: Install dependencies
run: npm ci
# blobファイルをダウンロード
- name: Download blob reports from GitHub Actions Artifacts
uses: actions/download-artifact@v4
with:
path: ./application/all-blob-reports
pattern: blob-report-*
merge-multiple: true
# レポートを生成
- name: Merge into HTML Report
run: npx playwright merge-reports --reporter html ./all-blob-reports
# 生成したレポートをアップロード
- name: Upload HTML report
uses: actions/upload-artifact@v4
with:
name: html-report--attempt-${{ github.run_attempt }}
path: application/playwright-report
retention-days: 30
GitHub Actionsのワークフローの説明
-
ワークフローのトリガー
今回は実装やユニットテストが完了した時のみワークフローを実行させれば良いので、トリガーをworkflow_dispatch
(手動)に設定しています。 -
Shardingの設定
以下の設定で、4つのシャードを作成しています。strategy: fail-fast: false matrix: shardIndex: [1, 2, 3, 4] shardTotal: [4]
-
shardIndex
: シャードの番号を指定(1~4)。 -
shardTotal
: 全体のシャード数を指定(今回は4)。
-
-
シャードの実行
各シャードが独立してテストを実行します。シャードごとにblob
形式でレポートを出力する設定になっています。 -
HTMLレポートの生成
全てのシャードの実行が完了した後に、merge-reports
ジョブを実行し、各シャードのblob
レポートを結合して1つのHTMLレポートを作成します。
この順序を守るために、needs
を使って依存関係を指定しています。merge-reports: # Merge reports after playwright-tests, even if some shards have failed if: ${{ !cancelled() }} # test jobが完了した後に実行 needs: [test]
完成
40分程度動き続けていたtestsを8分弱に短縮することができました!
html-report--attempt-1
をダウンロードして結果を確認することもできます。
②flakyが解消できない!
テストのステータスには、pass
、failed
、そして flaky
の3種類あります。
この flaky
とは テストが不安定で、断続的に失敗している状態 を指します。
主にflaky
が発生する要因は以下の通りです。
- タイミングの問題: 非同期処理やレースコンディションが原因で発生するケース
- 環境の変化: テスト環境やデバイスの違いによる影響
- 外部依存: 信頼性の低いサードパーティサービスやAPIへの依存
- 状態管理: テストケース間でのアプリケーション状態の不整合
僕の場合、エラーメッセージの表示に関するテストがflaky
(CI環境のみ)になっていました。
調査した結果、原因はエラーメッセージが確実に表示されている保証がない状態でアサーションを行っていたことでした。
この問題を解消するには、条件を満たすまで待機させる必要があります。
// 🙅♂️期待する条件を待たずにアサーションしてしまう。
expect(page.locator('#helper-text')).toHaveText('入力してください');
// 🙆♂️期待する条件を待ってからアサーションする。
await expect(page.locator('#helper-text')).toHaveText('入力してください');
ちなみに、Playwrightには条件を自動的に待機するアサーションが含まれているため、明示的な waitFor/waitForElementToBeRemovedの呼び出しは必要ないとのことです。
Playwright includes assertions that automatically wait for the condition, so you don't usually need an explicit waitFor/waitForElementToBeRemoved call.
最後にflaky
を解消する際、参考にしたサイトを貼っておきます。
③あれ?この環境はエミュレートできないのか!
Playwrightにはブラウザを仮想的にエミュレートする機能があり、特定の環境(OSやブラウザ)をシミュレーションできます。
ただし、すべての環境に対応しているわけではなく、対応範囲は限られています。
王道(Windows/Chromeなど)は基本的にサポートされていますが、プロジェクトの仕様とエミュレーション可能な環境が一致しているかを事前に確認するべきでした。
僕が担当しているプロジェクトでは、対応していないmacOS/Chrome環境でe2eテストをする必要があったので、macOS/Chrome環境のみ手動でテストを行いました。
終わりに
ローカルではテスト通るけどCI/CDでは通らない時が1番困るんだよな〜〜
Discussion