🌟

PlaywrightでE2Eテストを自動化する際に気を付けたいこと

2025/03/07に公開

Playwrightとは?

気を付けたいこと

適切なセレクタを使う

  • ❌cssセレクタ
    await page.click('div.button');
    
    • 見た目の変更が無くても、クラス名やHTML要素が変更されるとテストが失敗する
    • buttonクラスを持つ要素が複数存在してもテストが失敗する
  • ✅data-testid
    await page.getByTestId('submit-button').click();
    
    • ベストプラクティスとして紹介されているパターンの1つ
    • 開発者がアサート用に意図的に設定する値のため変更されにくい(テストが壊れにくい)
  • ✅role
    await page.getByRole('button', { name: '送信' }).click();
    
    • ベストプラクティスとして紹介されているパターンの1つ
    • ブラウザが認識しているアクセシビリティツリーを基にroleを取得するため、テスト対象のUIが適切なアクセシビリティ属性を持っていることを確認できる

※適切なセレクタを使うにはテスタブルなプロダクションコードを書く必要がある

明示的なwaitを避ける

  • ❌固定のwait
    await page.waitForTimeout(2000);
    await page.getByRole('button', { name: 'ログイン' }).click();
    
    • 要素が表示されても必ず指定された時間待つことになり、テストの実行時間が長くなる
  • ✅暗黙的にインタラクション可能になるまで待つ
    await page.getByRole('button', { name: 'ログイン' }).click();
    
    • 要素がインタラクション可能になるまで待つ
    • 待ち時間の最大値はオプションで指定が可能

並列実行できるテストシナリオの設計

  • ❌テスト間で状態を共有する
    test('ログインしてホーム画面に遷移すること', async ({ page }) => {
        await page.goto('https://example.com/login');
        await page.getByPlaceholder('ログインID').fill('test_user');
        await page.getByRole('パスワード').fill('password');
        await page.getByRole('button', { name: 'ログイン' }).click();
        await expect(page).toHaveURL('https://example.com/home');
    });
    
    test('ホーム画面から設定ボタン押下で設定画面に遷移すること', async ({ page }) => {
        await page.getByRole('button', { name: '設定' }).click();
        await expect(page).toHaveURL('https://example.com/setting');
    });
    
    • ログインするテストが失敗するとそれ以降のテストも必ず失敗する
    • 並列実行できないため、テストの実行時間が長くなる
    • この例の場合はログイン処理を共通化することで解決できる

テストのリトライ

  • E2Eテストはネットワーク遅延や環境要因等で一時的に失敗することがある
    • そのため、リトライを設定することで一時的な失敗を許容する
  • test単位で指定する場合
    test('不安定なテスト', async ({ page }) => {
        // 省略
    }).retry(2);
    
  • describe単位で指定する場合
    test.describe('不安定なテスト群', () => {
        test.describe.configure({ retries: 2 });
    
        test('不安定なテスト1', async ({ page }) => {
            // 省略
        });
    
        test('不安定なテスト2', async ({ page }) => {
            // 省略
        });
    });
    
  • 全体に指定
    import { defineConfig } from '@playwright/test';
    export default defineConfig({
        retries: 2,
    });
    
    • 全体に指定すると1件テストが落ちるだけですべてのケースをリトライすることになるため、不安定なテストのみリトライを指定するほうがよさそう

Discussion