この記事では、Playwrightを使用したスクリーンショット撮影と比較方法を詳しく解説する。ページ全体や特定の要素をキャプチャする基本的なメソッドから、テスト中にUIが期待通りに表示されているかを確認するためのtoHaveScreenshot
の使用方法まで、実際のコード例を交えて紹介する。Playwrightを活用してビジュアルリグレッションテストを行い、UIの品質を確保するための手法を学べる内容だ。
スクリーンショットに関連する各種メソッド
Playwrightでは、ページや特定の要素のスクリーンショットを撮影するためのメソッドが用意されている。
page.screenshot([options])
このメソッドは、ページ全体または特定の部分のスクリーンショットを撮影するために使用する。撮影したスクリーンショットは指定したファイルパスに保存することも、画像データとしてバッファに保持することも可能。
使用シーン:
テストの中で、UIが正しく表示されているかを確認したいとき、または特定の操作後のページ状態を記録したいときに使用する。
await page.screenshot({ path: 'screenshot.png', fullPage: true });
オプション:
path
: スクリーンショットを保存するファイルパス。指定がない場合、バッファに保存される。fullPage
: ページ全体(スクロールを含む)のスクリーンショットを撮影する。デフォルトはfalse
。omitBackground
: 背景を透明にする。PNG形式でのみ有効。デフォルトはfalse
。
特定の要素のみのスクリーンショットを撮影する場合は、以下のように記述する。
await page.locator('.header').screenshot({ path: 'header.png' });
公式ドキュメント: https://playwright.dev/docs/screenshots
expect(page).toHaveScreenshot([options])
このメソッドは、ページのスクリーンショットが期待されるスナップショットと一致するかどうかを検証するために使用する。スクリーンショットの一致をチェックすることで、UIが期待通りにレンダリングされているかを自動的に確認できる。
使用シーン:
ビジュアルリグレッションテストにおいて、UIが変更されていないことを確認するために使用する。
await expect(page).toHaveScreenshot({ animations: 'disabled' });
オプション:
animations
: CSSアニメーションやWebアニメーションを無効化する。デフォルトはdisabled
。caret
: テキストカーソルを隠すかどうか。デフォルトはhide
。mask
: スクリーンショット撮影時に隠したい要素を指定する。
公式ドキュメント: https://playwright.dev/docs/api/class-pageassertions#page-assertions-to-have-screenshot-2
waitForSelector(selector, [options])
特定のセレクタが指定の状態(例えば、visible
や attached
)になるまで待機する。スクリーンショットを撮影する前に、要素が確実に表示されていることを確認するために使用する。
使用シーン:
非同期でロードされる要素を待機してから操作やスクリーンショットを撮影する場合に使用する。
await page.waitForSelector('div.content', { state: 'visible' });
オプション
state
: 要素がvisible
,attached
,detached
,hidden
のどの状態になるまで待機するかを指定する。デフォルトはvisible
。timeout
: タイムアウトまでの待機時間(ミリ秒)。デフォルトは無限(0)。
https://playwright.dev/docs/api/class-elementhandle#element-handle-wait-for-selector
waitForLoadState([state, options])
ページやフレームが指定されたロード状態に達するまで待機する。ページの完全なロードが完了した後にスクリーンショットを撮影したい場合に使用する。
使用シーン:
ページが完全にロードされたことを確認してから、操作やスクリーンショットを撮影する場合に使用する。
await page.waitForLoadState('networkidle');
オプション
state
: 要素がvisible
,attached
,detached
,hidden
のどの状態になるまで待機するかを指定する。デフォルトはvisible
。timeout
: タイムアウトまでの待機時間(ミリ秒)。デフォルトは無限(0)。
https://playwright.dev/docs/api/class-frame#frame-wait-for-load-state
テスト実装
この章では、Playwrightを使ったE2Eテストで使用頻度の多いtoHaveScreenshot
と screenshot
を使用した実際のUIコード(HTML)とテストコードを紹介する。
動作環境は、Nuxt3で行なっている。
screenshot
を使用したテストコード
↓のページ全体のスクリーンショットを撮影する。
テストを実行する
import { test } from "@playwright/test";
test("screenshot", async ({ page }) => {
// ページに移動
await page.goto("http://localhost:3000/test/playwright/screen-shot");
// スクリーンショットを撮影
await page.screenshot({
path: "tests/screenshots/fullpage-screenshot.png",
fullPage: true,
});
});
実行後、tests/screenshots/fullpage-screenshot.pngを確認すると
スクリーンショットが保存されていることがわかる。
toHaveScreenshot
を使用したテストコード
このテストコードでは、ページのスクリーンショットを撮影し、期待されるスナップショットと比較する。
私の現場では、toHaveScreenshot
をベースにしている。
テストを実行する
test("toHaveScreenshot", async ({ page }) => {
// ページに移動
await page.goto("http://localhost:3000/test/playwright/screen-shot");
// スクリーンショットが期待されるスナップショットと一致するか確認
await expect(page).toHaveScreenshot();
});
toHaveScreenshotの場合、1度目のテストは以下のように失敗する。
なぜ、1度目のテストで失敗するのか。
この現象は、最初のtoHaveScreenshot
実行時にスナップショットが存在しないため、テストが失敗することが原因。toHaveScreenshot
を使う際、最初の実行でPlaywrightは指定された場所にスナップショット(期待されるスクリーンショット)が存在するか確認する。最初のテスト実行ではまだスナップショットが保存されていないため、Playwrightは新しいスナップショットを作成し、それをファイルに保存する。この過程でテストは「スナップショットが存在しない」というエラーとして失敗する。
2回目実行
テストは成功する。これは上記で説明した通り、2回目のテスト実行時にはすでに比較対象のスクリーンショットが存在するため、比較して変更がなければテストは通ることになる。
表示を変更して、3回目のテストを実施する
3回目のテストは、ページの表示内容を変更してみる。
Playwright update, screenshot update, toHaveScreenshot update
と表示を変えてみよう。
テストは失敗する。ページの表示が変わっているので当然の結果だ。
差分を目視比較する
playwrightのtoHaveScreenshot
は、スクリーンショットの比較に失敗した時に、差分を表示してくれる便利な機能がある。この機能により、テストが失敗した箇所を瞬時に把握できる。
テスト実行後、Attachmentsタブを押下 > Diffを選択。すると、以下のように差分が赤で表示される。
toHaveScreenshot
を使うことで、UIが期待通りに表示されているかを自動的にチェックできる。
まとめ
Playwrightを使用したスクリーンショットの撮影とビジュアルリグレッションテストは、UIが期待通りに表示されていることを確認する強力な手段である。この記事では、page.screenshot
を使ってページ全体や特定の要素をキャプチャする基本的な方法から、toHaveScreenshot
を利用したスクリーンショットの比較方法を解説した。ぜひ、これらの方法を活用して、あなたのテストプロセスを強化してほしい。
この記事は役に立ちましたか?
もし参考になりましたら、下記のボタンで教えてください。
コメント