T-CREATOR

Playwright スクリーンショット/動画取得のベストプラクティス集【設定例付き】

Playwright スクリーンショット/動画取得のベストプラクティス集【設定例付き】

テスト自動化において、スクリーンショットや動画の記録は不可欠な機能です。特にテストが失敗したときに、何が起きたのかを視覚的に確認できることは、デバッグ作業を大幅に効率化してくれます。

Playwright は、スクリーンショットと動画取得のための豊富な機能を提供しています。しかし、適切な設定や使い方を知らないと、ストレージを圧迫したり、テスト実行速度が低下したりする問題が発生しがちです。

本記事では、Playwright のスクリーンショット/動画取得機能を最大限に活用するためのベストプラクティスと、実践的な設定例を詳しく解説します。初心者の方でも、すぐに実務で使える知識が身につくでしょう。

早見表:設定のクイックリファレンス

推奨設定一覧

記事全体の要点を素早く確認できる早見表です。詳細は各章をご参照ください。

#設定項目推奨値理由参照章
1スクリーンショット取得screenshot: 'only-on-failure'ストレージ節約、失敗時のデバッグ情報は確保解決策 > 1
2動画記録(CI 環境)video: 'retain-on-failure'失敗時のみ保持でストレージ効率化解決策 > 1
3動画記録(ローカル環境)video: 'off'開発体験向上、必要時のみ手動で有効化解決策 > 3
4動画解像度size: { width: 1280, height: 720 }デバッグ品質とファイルサイズのバランス解決策 > 2
5出力ディレクトリoutputDir: 'test-results'証跡ファイルの一元管理解決策 > 1
6タイミング制御waitForLoadState('networkidle')動的コンテンツ読み込み完了を待機解決策 > 2
7ページ全体キャプチャfullPage: trueLP や SPA など縦長ページの完全記録解決策 > 3
8CI アーティファクト保持期間retention-days: 7ストレージコスト抑制とデバッグ期間確保の両立解決策 > 3
9ビジュアルリグレッションtoHaveScreenshot()CSS 変更の自動検出具体例 > 3
10エラーハンドリングリトライ機能実装(3 回推奨)一時的なエラーへの対応、テスト安定性向上具体例 > 5

環境別推奨設定

#環境スクリーンショット動画トレース用途
1ローカル開発'only-on-failure''off''off'高速な開発サイクル
2CI/CD'only-on-failure''retain-on-failure''retain-on-failure'完全なデバッグ情報取得
3デバッグモード'on''on''on'詳細な調査が必要な場合
4本番監視'only-on-failure''retain-on-failure''retain-on-failure'問題検出時の証跡確保
5パフォーマンス'off''off''off'実行速度の計測

ユースケース別ベストプラクティス

#ユースケース推奨手法コード例参照
1フォーム送信テスト段階的スクリーンショット取得具体例 > 3
2EC サイト購入フロー動画 + 重要ポイントでスクリーンショット具体例 > 4
3UI コンポーネントテスト特定要素のみキャプチャ解決策 > 4
4ランディングページ確認フルページスクリーンショット解決策 > 3
5ビジュアルリグレッションtoHaveScreenshot() 使用具体例 > 3
6複雑な画面遷移のデバッグ動画記録を有効化具体例 > 4
7エラー再現が困難な場合エラーハンドリング + リトライ機能具体例 > 5

よくある問題と解決策

#問題原因解決策参照章
1Error ENOSPC: no space left on device動画の記録過多'retain-on-failure' に変更課題 > 2
2TimeoutError: page.screenshotページ読み込み未完了waitForLoadState() 追加課題 > 2
3テスト実行速度が 30%低下動画エンコード処理ローカルでは動画オフ課題 > 1
4スクリーンショットが不完全タイミング制御不足waitForSelector() で要素待機解決策 > 2
5ファイルが散在して管理困難ディレクトリ構造未整備outputDir で一元管理解決策 > 1
6CI で証跡が見つからないアーティファクト未保存GitHub Actions で upload-artifact 設定解決策 > 3
7動画ファイルサイズが巨大高解像度設定1280x720 に最適化解決策 > 2

背景

なぜスクリーンショット・動画が必要なのか

E2E テストにおいて、ログやアサーションエラーだけでは問題の原因を特定できないケースが多々あります。特に以下のような状況では、視覚的な証拠が極めて重要になります。

まず、CI パイプライン上でのみ発生するエラーは、開発者のローカル環境では再現できないことがあります。この場合、スクリーンショットや動画がなければ、何が起きているのかを推測することしかできません。

また、タイミング依存のバグは、コードを読むだけでは見つけにくいものです。アニメーションの途中状態やローディング中の画面など、動的な挙動を確認するには動画が不可欠でしょう。

さらに、テスト結果のレポートに視覚的な証拠を添付することで、開発チームやステークホルダーとのコミュニケーションがスムーズになります。

以下の図は、テスト実行から証跡取得までの基本的なフローを示しています。

mermaidflowchart TB
    testStart["テスト開始"] --> testExec["テスト実行"]
    testExec --> decision{"テスト結果"}
    decision -->|成功| successPath["成功処理"]
    decision -->|失敗| failPath["失敗検出"]
    failPath --> capture["スクリーンショット<br/>動画取得"]
    capture --> report["レポート生成"]
    successPath --> optCapture["オプション:<br/>証跡取得"]
    optCapture --> report
    report --> testEnd["テスト終了"]

このフローからわかるように、失敗時には必ず証跡を取得し、成功時にもオプションで記録することで、テストの透明性を確保できます。

スクリーンショット・動画取得の使い分け

Playwright では、スクリーンショットと動画の 2 つの記録方法を提供しています。それぞれに適した用途があるため、状況に応じて使い分けることが重要です。

スクリーンショットは、特定の時点の画面状態を静止画として保存します。ファイルサイズが小さく、特定のアサーション前後の状態確認に最適でしょう。ページ全体のスクロールスクリーンショットも取得できます。

一方、動画はテスト全体の流れを連続的に記録できます。ユーザー操作の流れや画面遷移を確認でき、タイミングに依存するバグの調査に威力を発揮します。ただし、ファイルサイズが大きくなりがちです。

以下の表で、両者の特徴を整理しました。

#項目スクリーンショット動画
1ファイルサイズ小(数十 KB〜数 MB)大(数 MB〜数十 MB)
2取得タイミング任意の時点テスト実行中常時
3用途特定状態の確認操作フロー全体の確認
4デバッグ効率
5ストレージ負荷
6CI 環境での推奨度★★★★★★★★☆☆

課題

スクリーンショット・動画取得の一般的な問題点

Playwright のスクリーンショット・動画機能を使い始めると、いくつかの課題に直面することがあります。これらの課題を理解しておくことで、事前に対策を講じることができるでしょう。

最も深刻な問題は、ストレージの圧迫です。すべてのテストで動画を記録すると、数百のテストケースがある場合、数十 GB ものディスク容量を消費してしまいます。特に CI 環境では、この問題が顕著になります。

次に、テスト実行速度の低下も無視できません。動画のエンコード処理は CPU リソースを消費するため、テストの実行時間が 10〜30%程度増加することがあります。

また、タイミングの問題も発生しがちです。ページの読み込みが完了する前にスクリーンショットを取得してしまい、意図しない状態が記録されることがあります。特に動的コンテンツが多い SPA では注意が必要でしょう。

さらに、ファイル管理の煩雑さという課題もあります。スクリーンショットや動画が散在すると、どのファイルがどのテストに対応しているのか分かりにくくなります。

以下の図は、これらの課題が発生する流れを示しています。

mermaidflowchart LR
    allTests["全テストで<br/>動画記録"] --> storage["ストレージ圧迫"]
    allTests --> slowdown["実行速度低下"]
    poorTiming["タイミング制御<br/>不適切"] --> wrongCapture["不完全な画面<br/>キャプチャ"]
    noOrganize["ファイル管理<br/>未整備"] --> chaos["ファイル散在"]
    storage --> ciFailure["CI容量不足"]
    slowdown --> timeout["タイムアウト"]
    wrongCapture --> debugDifficult["デバッグ困難"]
    chaos --> maintenance["保守性低下"]

図で理解できる要点:

  • 全テストでの動画記録は複数の問題を引き起こす
  • タイミング制御とファイル管理が適切でないと、せっかくの証跡が役に立たなくなる
  • 各問題が連鎖して、最終的に CI 失敗や保守性低下につながる

具体的なエラー事例

実際の開発現場で遭遇しやすいエラーを見てみましょう。

Error ENOSPC: no space left on deviceというエラーは、CI 環境でディスク容量が不足したときに発生します。数百のテストすべてで動画を記録していると、このエラーに遭遇することがあります。

typescript// エラー発生条件:全テストで動画記録を有効化
use: {
  video: 'on', // すべてのテストで動画を記録
}

このような設定では、100 件のテストで平均 5MB の動画が生成されると、500MB ものストレージを消費してしまいます。

また、TimeoutError: page.screenshot: Timeout 30000ms exceededというエラーも頻出します。ページの読み込みが完了していない状態でスクリーンショットを取得しようとすると、このエラーが発生します。

typescript// 問題のあるコード例
await page.goto('https://example.com');
await page.screenshot({ path: 'screenshot.png' }); // ページ読み込み完了を待っていない

解決策

スクリーンショット取得のベストプラクティス

スクリーンショットを効果的に活用するためのベストプラクティスを紹介します。これらの手法を組み合わせることで、デバッグ効率を最大化しつつ、リソース消費を最小限に抑えられるでしょう。

1. 失敗時のみ自動取得する設定

最も基本的かつ重要なプラクティスは、テスト失敗時のみスクリーンショットを取得することです。Playwright の設定ファイルで簡単に実現できます。

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

export default defineConfig({
  use: {
    // 失敗時のみスクリーンショットを取得
    screenshot: 'only-on-failure',
  },
});

この設定により、成功したテストではスクリーンショットを保存しないため、ストレージを大幅に節約できます。失敗時には自動的に取得されるため、デバッグに必要な情報は確実に残ります。

2. 適切なタイミングでの取得

ページの状態が安定してからスクリーンショットを取得することが重要です。waitForLoadStatewaitForSelectorを活用しましょう。

typescript// テストファイル内での実装例
import { test, expect } from '@playwright/test';

test('商品詳細ページのスクリーンショット', async ({ page }) => {
  await page.goto('https://example.com/products/123');

  // ネットワークアイドル状態を待つ
  await page.waitForLoadState('networkidle');

ネットワークアイドル状態まで待つことで、動的コンテンツの読み込みが完了してからスクリーンショットを取得できます。

typescript  // 特定の要素が表示されるまで待つ
  await page.waitForSelector('.product-image', { state: 'visible' });

  // スクリーンショットを取得
  await page.screenshot({
    path: 'screenshots/product-detail.png',
    fullPage: true
  });
});

特定の要素が表示されることを確認してから取得することで、不完全な画面のキャプチャを防げます。

3. ページ全体のスクリーンショット

SPA やランディングページなど、縦に長いページでは、fullPageオプションを使用してページ全体をキャプチャします。

typescript// ページ全体をキャプチャする例
test('ランディングページ全体', async ({ page }) => {
  await page.goto('https://example.com/landing');
  await page.waitForLoadState('networkidle');

  // ページ全体をスクロールしてキャプチャ
  await page.screenshot({
    path: 'screenshots/landing-full.png',
    fullPage: true,
  });
});

fullPage: trueを指定すると、Playwright が自動的にスクロールしながらページ全体を 1 枚の画像として保存してくれます。

4. 特定要素のみをキャプチャ

デバッグ時には、ページ全体ではなく特定のコンポーネントのみをキャプチャしたい場合があります。Locator のscreenshotメソッドを使用しましょう。

typescript// 特定要素のスクリーンショット
test('ヘッダーコンポーネントのみ', async ({ page }) => {
  await page.goto('https://example.com');

  // ヘッダー要素を取得
  const header = page.locator('header.main-header');

  // ヘッダーのみをキャプチャ
  await header.screenshot({
    path: 'screenshots/header-component.png',
  });
});

この方法は、コンポーネント単位でのビジュアルリグレッションテストにも活用できます。ファイルサイズも小さくなるため、効率的です。

動画取得のベストプラクティス

動画記録は非常に強力なデバッグツールですが、適切に設定しないとリソースを圧迫します。以下のベストプラクティスに従って、効率的に活用しましょう。

1. 失敗時のみ動画を保持する設定

最も推奨される設定は、すべてのテストで動画を記録するが、失敗したテストの動画のみを保持する方法です。

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

export default defineConfig({
  use: {
    // 失敗時のみ動画を保持
    video: 'retain-on-failure',
  },
});

この設定では、テスト実行中は動画を記録し続けますが、テストが成功した場合は動画ファイルを自動的に削除します。失敗したテストの動画のみが残るため、ストレージを節約できます。

2. 動画サイズの最適化

動画のサイズを最適化することで、ストレージとネットワーク帯域を節約できます。解像度を調整しましょう。

typescript// 動画サイズを最適化する設定
export default defineConfig({
  use: {
    video: {
      mode: 'retain-on-failure',
      // 動画サイズを指定(デフォルトはビューポートサイズ)
      size: { width: 1280, height: 720 },
    },
  },
});

1280x720 の HD 解像度であれば、デバッグには十分な品質を保ちつつ、ファイルサイズを抑えられます。4K などの高解像度は通常不要でしょう。

3. CI 環境での選択的記録

CI 環境では、すべてのテストで動画を記録するのではなく、環境変数で制御することをお勧めします。

typescript// CI環境のみで動画を記録する設定
export default defineConfig({
  use: {
    video: process.env.CI
      ? 'retain-on-failure' // CI環境では失敗時のみ保持
      : 'off', // ローカルでは記録しない
  },
});

ローカル開発環境では動画を記録せず、CI 環境でのみ記録することで、開発者の体験を損なわずにデバッグ情報を確保できます。

4. 動画の保存先を整理する

動画ファイルを整理された構造で保存することで、管理が容易になります。テスト名やタイムスタンプを含めた命名規則を使いましょう。

typescript// 動画の保存先をカスタマイズ
import { test as baseTest } from '@playwright/test';

const test = baseTest.extend({
  context: async ({ context }, use, testInfo) => {
    // テスト情報から動画パスを生成
    const videoPath = `videos/${testInfo.project.name}/${testInfo.file}/${testInfo.title}.webm`;

    await use(context);
  },
});

プロジェクト名、ファイル名、テストタイトルを組み合わせることで、後から特定のテストの動画を見つけやすくなります。

以下の図は、動画取得の最適な設定フローを示しています。

mermaidflowchart TD
    start["動画設定検討"] --> envCheck{"実行環境"}
    envCheck -->|ローカル| localSet["video: 'off'"]
    envCheck -->|CI| ciSet["video: 'retain-on-failure'"]
    ciSet --> sizeOpt["サイズ最適化<br/>1280x720"]
    sizeOpt --> pathOrg["保存先整理<br/>プロジェクト/ファイル/テスト"]
    pathOrg --> complete["最適化完了"]
    localSet --> manualRec["必要時のみ<br/>手動で有効化"]
    manualRec --> complete

図で理解できる要点:

  • 実行環境によって動画記録の戦略を変える
  • CI 環境では失敗時のみ保持し、サイズを最適化
  • ローカル環境では基本的にオフにして、必要時のみ有効化

ファイル管理のベストプラクティス

スクリーンショットや動画のファイル管理も重要な要素です。適切な管理により、デバッグ効率が大きく向上します。

1. 出力ディレクトリの統一

すべての証跡ファイルを統一されたディレクトリ構造で管理しましょう。

typescript// playwright.config.ts
export default defineConfig({
  // テスト結果の出力先
  outputDir: 'test-results',

  use: {
    screenshot: 'only-on-failure',
    video: 'retain-on-failure',
  },
});

outputDirを指定することで、スクリーンショット、動画、トレースファイルなどがすべて同じディレクトリ配下に保存されます。

2. .gitignore への追加

証跡ファイルは Git 管理対象外にすることが推奨されます。ファイルサイズが大きく、リポジトリを肥大化させてしまうためです。

gitignore# .gitignore
test-results/
screenshots/
videos/
playwright-report/

これらのディレクトリを.gitignoreに追加することで、誤ってコミットすることを防げます。

3. CI 環境でのアーティファクト保存

CI 環境では、テスト失敗時の証跡をアーティファクトとして保存し、開発者がダウンロードできるようにします。

yaml# GitHub Actionsの例
- name: Run Playwright tests
  run: yarn playwright test

- name: Upload test results
  if: failure()
  uses: actions/upload-artifact@v3
  with:
    name: playwright-results
    path: test-results/
    retention-days: 7

失敗時のみアーティファクトをアップロードし、7 日間保持することで、ストレージコストを抑えつつデバッグ情報を確保できます。

具体例

実践的な設定例とコード

ここまで解説したベストプラクティスを組み合わせた、実践的な設定例を紹介します。実際のプロジェクトですぐに使える内容になっています。

基本的なプロジェクト設定

まず、Playwright の設定ファイル全体を見てみましょう。この設定は、多くのプロジェクトで推奨される基本構成です。

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

export default defineConfig({
  // テスト結果の出力先
  outputDir: 'test-results',

  // テストディレクトリ
  testDir: './tests',

  // 並列実行の設定
  fullyParallel: true,

テスト結果をtest-resultsディレクトリに集約し、並列実行を有効にすることで、効率的なテスト実行を実現します。

typescript  // タイムアウト設定
  timeout: 30000,
  expect: {
    timeout: 5000
  },

  // レポーター設定
  reporter: [
    ['html', { outputFolder: 'playwright-report' }],
    ['list']
  ],

適切なタイムアウトを設定し、HTML レポートとリスト形式のレポートを両方出力します。

typescript  use: {
    // ベースURL
    baseURL: 'http://localhost:3000',

    // トレース記録(失敗時のみ)
    trace: 'retain-on-failure',

    // スクリーンショット(失敗時のみ)
    screenshot: 'only-on-failure',

    // 動画(CI環境のみ、失敗時保持)
    video: process.env.CI ? 'retain-on-failure' : 'off',
  },

CI 環境でのみ動画を記録し、スクリーンショットとトレースは失敗時のみ取得する設定です。これにより、リソース効率とデバッグ効率のバランスが取れます。

typescript  // ブラウザ設定
  projects: [
    {
      name: 'chromium',
      use: { ...devices['Desktop Chrome'] },
    },
    {
      name: 'firefox',
      use: { ...devices['Desktop Firefox'] },
    },
  ],
});

複数のブラウザでテストを実行することで、クロスブラウザの互換性を確保できます。

カスタムスクリーンショットヘルパー関数

テスト内で柔軟にスクリーンショットを取得できるヘルパー関数を作成しましょう。

typescript// tests/helpers/screenshot.ts
import { Page } from '@playwright/test';
import path from 'path';

/**
 * スクリーンショットを取得するヘルパー関数
 * タイムスタンプ付きのファイル名で保存する
 */
export async function takeScreenshot(
  page: Page,
  name: string,
  options?: { fullPage?: boolean }
): Promise<string> {

ヘルパー関数を定義することで、テストコード全体でスクリーンショット取得のロジックを統一できます。

typescript  // タイムスタンプを生成
  const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
  const fileName = `${name}-${timestamp}.png`;
  const screenshotPath = path.join('screenshots', fileName);

  // ディレクトリが存在しない場合は作成
  await page.screenshot({
    path: screenshotPath,
    fullPage: options?.fullPage ?? false,
  });

  return screenshotPath;
}

タイムスタンプを含めることで、同じテストを複数回実行してもファイルが上書きされず、履歴を保持できます。

段階的なスクリーンショット取得

フォーム送信など、複数ステップからなる操作では、各段階でスクリーンショットを取得すると、どの段階で問題が発生したかを特定しやすくなります。

typescript// tests/form-submission.spec.ts
import { test, expect } from '@playwright/test';
import { takeScreenshot } from './helpers/screenshot';

test('ユーザー登録フォームの送信', async ({ page }) => {
  // ステップ1: フォームページに移動
  await page.goto('/register');
  await page.waitForLoadState('networkidle');
  await takeScreenshot(page, 'step1-form-loaded');

最初のステップで、フォームが正しく読み込まれたことを確認します。

typescript// ステップ2: フォーム入力
await page.fill('#username', 'testuser');
await page.fill('#email', 'test@example.com');
await page.fill('#password', 'SecurePass123!');
await takeScreenshot(page, 'step2-form-filled');

入力後の状態をキャプチャすることで、入力値が正しく反映されているかを確認できます。

typescript// ステップ3: 送信ボタンをクリック
await page.click('button[type="submit"]');
await page.waitForURL('/dashboard');
await takeScreenshot(page, 'step3-registration-success', {
  fullPage: true,
});

送信後のダッシュボード画面全体をキャプチャし、登録が成功したことを視覚的に確認します。

typescript  // アサーション
  await expect(page.locator('h1')).toContainText('ダッシュボード');
});

ビジュアルリグレッションテストの実装

Playwright のtoHaveScreenshotを使用して、ビジュアルリグレッションテストを実装できます。

typescript// tests/visual-regression.spec.ts
import { test, expect } from '@playwright/test';

test('トップページのビジュアルリグレッション', async ({
  page,
}) => {
  await page.goto('/');
  await page.waitForLoadState('networkidle');

  // スクリーンショットを比較
  await expect(page).toHaveScreenshot('homepage.png', {
    maxDiffPixels: 100, // 最大100ピクセルの差異を許容
  });
});

初回実行時にベースライン画像が作成され、2 回目以降は差分を検出してくれます。CSS の意図しない変更を早期発見できるでしょう。

動画とスクリーンショットを組み合わせた高度な例

複雑なユーザーフローでは、動画とスクリーンショットを組み合わせることで、より効果的なデバッグが可能になります。

typescript// tests/advanced-flow.spec.ts
import { test, expect } from '@playwright/test';

test.describe('ECサイトの購入フロー', () => {
  // このテストスイートでは動画を必ず記録
  test.use({ video: 'on' });

  test('商品検索から購入完了まで', async ({ page }) => {
    // 商品検索
    await page.goto('/');
    await page.fill('#search', 'ノートパソコン');
    await page.click('button[type="submit"]');

動画で全体のフローを記録しつつ、重要なポイントでスクリーンショットも取得します。

typescript// 検索結果の確認
await page.waitForSelector('.product-list');
const products = page.locator('.product-item');
await expect(products).toHaveCount(10);

// 検索結果のスクリーンショット
await page.screenshot({
  path: 'test-results/search-results.png',
  fullPage: true,
});

検索結果画面は静止画でしっかり確認できるようにします。

typescript// 商品詳細ページへ移動
await products.first().click();
await page.waitForLoadState('networkidle');

// カートに追加
await page.click('button.add-to-cart');
await page.waitForSelector('.cart-notification');

// カート追加通知のスクリーンショット
await page.locator('.cart-notification').screenshot({
  path: 'test-results/cart-notification.png',
});

通知コンポーネントだけをキャプチャすることで、ファイルサイズを抑えつつ重要な情報を記録します。

typescript    // チェックアウト
    await page.click('a[href="/cart"]');
    await page.waitForLoadState('networkidle');
    await page.click('button.checkout');

    // 購入完了の確認
    await page.waitForURL('/order-complete');
    await expect(page.locator('h1')).toContainText('ご注文ありがとうございます');
  });
});

このテストが失敗した場合、動画で全体の流れを確認し、スクリーンショットで特定の状態を詳しく調査できます。

以下の図は、実践的なテストフローでの証跡取得タイミングを示しています。

mermaidsequenceDiagram
    participant T as テスト
    participant P as Page
    participant V as 動画記録
    participant S as スクリーンショット

    T->>V: 記録開始
    T->>P: ページ遷移
    P-->>T: 読み込み完了
    T->>S: step1キャプチャ
    T->>P: フォーム入力
    T->>S: step2キャプチャ
    T->>P: 送信処理
    P-->>T: 完了画面表示
    T->>S: step3キャプチャ
    T->>T: アサーション実行
    alt テスト成功
        T->>V: 動画削除
        T->>S: スクリーンショット削除
    else テスト失敗
        T->>V: 動画保存
        T->>S: エラー時追加キャプチャ
    end

図で理解できる要点:

  • 動画は常に記録し続け、成功時のみ削除する
  • スクリーンショットは重要なステップごとに取得
  • 失敗時には自動的にすべての証跡が保存される

エラーハンドリングとリトライ

スクリーンショット取得自体が失敗する可能性もあるため、適切なエラーハンドリングを実装しましょう。

typescript// tests/helpers/safe-screenshot.ts
import { Page } from '@playwright/test';

/**
 * エラーハンドリング付きスクリーンショット取得
 */
export async function safeScreenshot(
  page: Page,
  name: string,
  retries: number = 3
): Promise<boolean> {
  for (let i = 0; i < retries; i++) {
    try {

リトライ機能を実装することで、一時的なエラーに対応できます。

typescript      await page.screenshot({
        path: `screenshots/${name}.png`,
        timeout: 5000,  // 5秒のタイムアウト
      });
      return true;  // 成功

    } catch (error) {
      console.error(
        `スクリーンショット取得失敗 (試行 ${i + 1}/${retries}):`,
        error
      );

エラーメッセージにリトライ回数を含めることで、デバッグが容易になります。

typescript      // 最後のリトライでなければ待機
      if (i < retries - 1) {
        await page.waitForTimeout(1000);
      }
    }
  }

  return false;  // すべて失敗
}

すべてのリトライが失敗した場合でも、テスト自体は続行できるように false を返します。

パフォーマンス測定を含む実装

スクリーンショット取得にかかる時間を測定し、パフォーマンスの問題を早期発見できるようにします。

typescript// tests/helpers/performance-screenshot.ts
import { Page } from '@playwright/test';

/**
 * パフォーマンス測定付きスクリーンショット
 */
export async function measureScreenshot(
  page: Page,
  name: string
): Promise<{ success: boolean; duration: number }> {
  const startTime = performance.now();

  try {
    await page.screenshot({ path: `screenshots/${name}.png` });
    const duration = performance.now() - startTime;

performance.now()を使用することで、ミリ秒単位の正確な測定が可能です。

typescript// 3秒以上かかった場合は警告
if (duration > 3000) {
  console.warn(
    `警告: スクリーンショット取得に${duration.toFixed(
      0
    )}ms かかりました`
  );
}

return { success: true, duration };

長時間かかった場合は警告を出力し、パフォーマンス問題に気づきやすくします。

typescript  } catch (error) {
    const duration = performance.now() - startTime;
    console.error('スクリーンショット取得エラー:', error);
    return { success: false, duration };
  }
}

まとめ

Playwright のスクリーンショット・動画取得機能は、E2E テストのデバッグ効率を大幅に向上させる強力なツールです。本記事で紹介したベストプラクティスを活用することで、リソース効率を保ちながら、必要な証跡をしっかりと記録できるようになります。

重要なポイントを振り返ってみましょう。

まず、失敗時のみ記録する設定が最も基本的かつ効果的な方法です。screenshot: 'only-on-failure'video: 'retain-on-failure'を使用することで、ストレージを節約しつつデバッグに必要な情報を確保できます。

次に、環境に応じた設定の使い分けも重要です。ローカル環境では動画をオフにし、CI 環境でのみ有効化することで、開発者体験を損なわずに証跡を残せます。

また、適切なタイミング制御により、不完全な画面のキャプチャを防げます。waitForLoadStatewaitForSelectorを活用して、ページの状態が安定してから取得しましょう。

ファイル管理の整理も見落とせません。統一されたディレクトリ構造と命名規則により、後から特定のテストの証跡を探しやすくなります。CI 環境ではアーティファクトとして保存し、適切な保持期間を設定することが推奨されます。

さらに、ヘルパー関数の活用により、テストコード全体で一貫した証跡取得が可能になります。エラーハンドリングやパフォーマンス測定を含めることで、より堅牢な実装になるでしょう。

最後に、ビジュアルリグレッションテストの導入により、UI の意図しない変更を自動的に検出できます。toHaveScreenshotを使えば、簡単に実装できますね。

これらのベストプラクティスを組み合わせることで、効率的で保守性の高い E2E テストを実現できます。最初は基本的な設定から始めて、プロジェクトの要件に応じて段階的に高度な機能を追加していくとよいでしょう。

テスト自動化の成功には、適切な証跡管理が欠かせません。ぜひ本記事の内容を参考に、あなたのプロジェクトに最適なスクリーンショット・動画取得の仕組みを構築してください。

関連リンク