Playwrightのベストプラクティスを翻訳してみた

Playwrightの公式ドキュメントに「Best Practices」というページがあったので翻訳してみました。

原文: Best Practices | Playwright

目次

  1. 目次
  2. イントロダクション
  3. テスト哲学​
    1. ユーザから見えるふるまいをテストする
    2. テストはできるだけ分離する
    3. サードパーティの依存関係をテストしない
    4. データベースを使ったテスト
  4. ベストプラクティス​
    1. ロケータを使う
      1. メソッドチェーンとフィルタリングの使用
      2. XPath や CSS セレクタよりもユーザー向けの属性値を優先する​
    2. ロケータの生成​
      1. codegen を使ってロケータを生成する
      2. VS Code 拡張機能を使用してロケーターを生成する
    3. ウェブファーストのアサーションを使う
      1. 手動でアサーションを使わない
    4. デバッグの設定​
      1. ローカル環境でデバッグする
      2. CIでのデバッグ​
    5. Playwrightのツールを使う​
    6. すべてのブラウザでテスト
    7. Playwright の依存関係を最新に保つ​
    8. CIでテストを実行する​
    9. Lintを利用する​
    10. 並列処理とシャーディングの使用
  5. 生産性向上のヒント​
    1. ソフトアサーションを使う​

イントロダクション

このガイドは、私たちが提供するベストプラクティスに習い、より弾力性のあるテストを書くために役立つはずです。

テスト哲学

ユーザから見えるふるまいをテストする

自動テストは、アプリケーションのコードがエンドユーザのために動作することを検証するものです。関数の名前、何かが配列であるかどうか、ある要素の CSS クラスのような、ユーザが通常使用しない、目にしない、あるいは知ることさえないような実装の詳細に依存することを避けるべきです。エンドユーザーはページ上でレンダリングされたものを見たり操作したりします。したがって、自動テストでは通常、レンダリングされた出力のみを表示/操作する必要があります。

テストはできるだけ分離する

それぞれの自動テストは、他のテストから完全に分離されるべきです。そして、独自のローカルストレージ、セッションストレージ、データ、クッキーなどを使い、独立して実行されるべきです。テストの分離は再現性を高め、デバッグを容易にし、テストの失敗の連鎖を防ぎます。

テストの特定の部分の繰り返しを避けるために、 before フックと after フックを使うことができます。テストファイル内に before フックを追加して、特定のURLにアクセスしたりアプリの一部にログインしたりするようなテストの一部を、各テストの前に実行するようにします。こうすることで、どのテストも他のテストに依存しなくなり、テストの分離が保たれます。しかし、テストが十分に単純な場合は、多少重複があってもかまいません。

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

test.beforeEach(async ({ page }) => {
  // Runs before each test and signs in each page.
  await page.goto('https://github.com/login');
  await page.getByLabel('Username or email address').fill('username');
  await page.getByLabel('Password').fill('password');
  await page.getByRole('button', { name: 'Sign in' }).click();
});

test('first', async ({ page }) => {
  // page is signed in.
});

test('second', async ({ page }) => {
  // page is signed in.
});

また、サインインした状態をプロジェクトの初期セットアップによって再利用することもできます。そうすれば、一度だけログインすれば、すべてのテストでログインステップを省略することができます。

訳注: ログイン処理などは、毎回アカウント作成して、毎回ログインしたほうが独立性が保てるかなと思ったけど、Basic: shared account in all testsではアカウントの共有を推奨していますね。共有によって楽にはなるけど、あるテストの操作が他に影響でないように考慮して作らないと、テストの並列実行数を増やせなくなりそうです。

サードパーティの依存関係をテストしない

自分が管理しているものだけをテストしましょう。自分が管理していない外部サイトやサードパーティのサーバーへのリンクをテストしようとしないでください。テストに時間がかかり、遅くなるだけでなく、リンク先のページ、クッキーバナーやオーバーレイページなど、テストが失敗する可能性のあるものを制御できません。

代わりに、Playwright Network APIを使用し、必要なレスポンスを保証します。

await page.route('**/api/fetch_data_third_party_dependency', route => route.fulfill({
  status: 200,
  body: testData,
}));
await page.goto('https://example.com');

訳注: Playwright Network APIはAPI等の Mocking などができる機能。テスト範囲を絞り込むために、外部など管理外の領域を Mock 化していくのは E2E でも必要。

データベースを使ったテスト

データベースを使用する場合は、データを管理しましょう。ステージング環境でテストを行い、データが変わっていないことを確認します。視覚的なリグレッション・テストでは、オペレーティング・システムとブラウザのバージョンが同じであることを確認してください。

訳注: 原文「If working with a database then make sure you control the data. Test against a staging environment and make sure it doesn’t change.」。データに影響しないようにデータを管理しましょうということかな?

ベストプラクティス

ロケータを使う

エンド・ツー・エンドのテストを書くためには、まずウェブページ上の要素を見つける必要があります。これを行うには、 Playwright に組み込まれているロケータを使用します。ロケーターは、自動待機機能とリトライ機能を備えています。自動待機機能とは、Playwrightがクリックを実行する前に、要素が表示されていて有効になっていることを確認するなど、要素に対してさまざまな実行可能性チェックを行うことを意味します。テストに弾力性を持たせるために、ユーザー向けの属性値や、明示的な要素をロケータで優先して使うことをお勧めします。

// 👍
page.getByRole('button', { name: 'submit' });

メソッドチェーンとフィルタリングの使用

ロケータは、ページの特定の部分を検索で絞り込むために、メソッドチェーンを利用することができます。

const product = page.getByRole('listitem').filter({ hasText: 'Product 2' });

ロケータをテキストや別のロケータでフィルタリングすることもできます。

await page
    .getByRole('listitem')
    .filter({ hasText: 'Product 2' })
    .getByRole('button', { name: 'Add to cart' })
    .click();

XPath や CSS セレクタよりもユーザー向けの属性値を優先する

DOM は簡単に変更できるので、テストを DOM 構造に依存させるとテストが失敗する可能性があります。たとえば、 CSS のクラスを使ってボタンを選択することを考えてみましょう。デザイナーが何かを変更すると、クラスが変更されてテストが失敗するかもしれません。

// 👎
page.locator('button.buttonIcon.episode-actions-later');

そこで、DOMの変化に強いロケータを使用します。

// 👍
page.getByRole('button', { name: 'submit' });

ロケータの生成

Playwright にはテストジェネレータがあり、テストを生成してロケータを選んでくれます。あなたのページを見て、ロール、テキスト、テスト ID ロケータを優先して最適なロケータを決定します。ロケータにマッチする要素が複数見つかった場合は、ロケータの使い方を改善して要素の発見に弾力性を持たせ、ターゲット要素を一意に識別できるようにします。それにより、ロケータが原因でテストが失敗する心配がなくなります。

codegen を使ってロケータを生成する

ロケーターを選ぶには、 codegen コマンドの後にロケーターを選びたいURLを続けて実行します。

npx playwright codegen playwright.dev

codegen コマンドを叩くと、新しいブラウザウィンドウと Playwright インスペクタが開きます。最初のロケーターを選ぶには、「Record」ボタンをクリックして録画を停止します。デフォルトでは、 codegen コマンドを実行すると新しい記録が開始されます。録音を停止すると、「Pick Locator」ボタンがクリックできるようになります。

ブラウザウィンドウでページ上の任意の要素にカーソルを合わせると、カーソルの下にロケーターがハイライト表示されます。要素をクリックすると、ロケータが Playwright インスペクタに追加されます。ロケータをコピーしてテストファイルに貼り付けたり、 Playwright インスペクタ上のテキストを修正するなどしてロケータを編集し、ブラウザウィンドウで結果を見ながら探索を続けられます。

Playwrightインスペクタを起動すると小さなウィンドウが開く。ブラウザを操作していくと、ここにロケーターが追記されていく。

VS Code 拡張機能を使用してロケーターを生成する

VS Codeエクステンションを使用して、ロケーターを生成したり、テストを記録することもできます。また、VS Code 拡張機能を使用すると、テストの記述、実行、およびデバッグを行う際に、優れた開発者体験を得られます。

VS CodeのPlaywright拡張機能を利用するとエディタからロケータの取得ができる。

ウェブファーストのアサーションを使う

アサーションは、期待される結果と実際の結果が一致するかどうかを検証する方法です。ウェブファーストアサーションを使用すると、 Playwright は期待する条件が満たされるまで待機します。例えば、アラートメッセージをテストする場合、テストはメッセージを表示させるボタンをクリックし、アラートメッセージがあるかどうかをチェックすると思います。アラートメッセージが表示されるまでに 0.5 秒かかる場合、必要があれば要素の出現を待ったり、リトライを仕掛けたりする toBeVisible() のようなアサーションを使用します。

// 👍
await expect(page.getByText('welcome')).toBeVisible();

// 👎
expect(await page.getByText('welcome').isVisible()).toBe(true);

手動でアサーションを使わない

予期している要素の存在を待ってくれない手動アサーションを使わないでください。以下のコードを見てみると、awaitexpect ファンクションの前ではなく中にあります。こうしてしまうと、isVisible() のようなアサーションを使用する場合、テストは一秒たりとも待たなくなります。ロケーターの存在をすぐに確認し、すぐに結果を返してしまうからです。

// 👎expect(await page.getByText('welcome').isVisible()).toBe(true);

代わりにtoBeVisible()のようなウェブファーストのアサーションを使います。

// 👍await expect(page.getByText('welcome')).toBeVisible();

デバッグの設定

ローカル環境でデバッグする

ローカルデバッグを行うなら、 VS Code 拡張モジュールをインストールして、 VS Code でテストをデバッグすることをお勧めします。実行したいテストの横の行を右クリックすれば、デバッグモードでテスト実行が可能になり、ブラウザウィンドウを開き、ブレークポイントが設定された位置で一時停止します。

指定したブレークポイントまでテストを実行し、そのときの変数の中身などを確認できる。

VS Code でテスト内のロケータをクリックまたは編集することで、テストをライブデバッグできます。対象のロケータはブラウザウィンドウでハイライトされ、ページ上で見つかった他の一致するロケータも表示されます。

デバッグ時にVS Code上のロケータ取得部分の行(ここでは14行目)をクリックすると、画面の対象の場所(ここではGET STARTEDのボタン)がハイライトされる。

また、 --debug フラグを付けてテストを実行すると、Playwright インスペクタ上でテストをデバッグできます。

npx playwright test --debug

テストを実行し、アクショナビリティログを表示し、実行しながらロケータを編集し、ブラウザウィンドウでハイライト表示できます。これにより、どのロケータがマッチするか、いくつマッチするかがわかります。

訳注:アクショナビリティログは、Playwright インスペクタで確認できる実行ログ

–debugフラグを付けて実行すると、テストの冒頭から順番に実行可能。ここでは14行目の要素をクリックする処理のアクショナビリティログを表示している。要素を取得するときのWait(waiting for getByRole・・・)や表示のチェック(element is visible)など、Playwrightは裏側でテストが安定するように様々な仕掛けを動かしているのがわかる。

特定のテストをデバッグする場合は、テストファイル名とテストの行番号の後に–debugフラグを付けます。

npx playwright test example.spec.ts:9 --debug

CIでのデバッグ

CIで失敗した場合は、動画やスクリーンショットの代わりに Playwright のトレースビューアを使用できます。トレースビューアでは、テストの完全なトレースをローカルの Progressive Web App (PWA) として簡単に共有できます。トレースビューアを使用すると、実行したテストのタイムラインを表示したり、開発ツールを使用して各アクションの DOM スナップショットを検査したり、ネットワークリクエストを表示したりできます。

トレースビューアでトレースログを開くと上記のような画面になる。動きは動画のような形式(スナップショットを定期的に取得してパラパラ漫画のように見える)で確認でき、どこで何が起きているかがすぐわかる。

トレースビューアは Playwright 設定ファイルで設定され、失敗したテストの最初の再試行時に CI で実行されるように設定されます。すべてのテストでトレースが実行されるようにonと設定することは、パフォーマンスが非常に重くなるためお勧めしません。しかし、--trace フラグを指定しながら開発するのであれば、ローカルでトレースを実行しても問題にならないはずです。

npx playwright test --trace on

このコマンドを実行すると、各テストのトレースが記録され、HTML レポートから直接見ることができます。

npx playwright test --trace on --reporter=html
npx playwright show-report
失敗したテストの右下に表示される「View trace」をクリックするとトレースビューアが開く。

トレースは、テストの横にあるアイコンをクリックするか、各テストレポートを開いてトレースセクションまでスクロールすれば開けます。

HTMLレポートの下部からもトレースビューアが開ける。その下に添付されている traceリンクはトレースログ。

Playwrightのツールを使う

Playwright には、テスト作成に役立つさまざまなツールが用意されています。

  • VS Code 拡張は、テストの記述、実行、デバッグの際に優れた開発者体験を提供します。
  • テストジェネレータは、テストを生成したり、ロケータをピックアップしてくれます。
  • トレースビューアは、ローカル PWA としてテストの完全なトレースを提供し、簡単に共有できます。トレースビューアでは、タイムラインの表示、各アクションの DOM スナップショットの検査、ネットワークリクエストの表示などが可能です。
  • UIモードでは、画面をウォッチしながらテスト実行されるので、タイムトラベルのような体験を通してテストを探索、実行、デバッグできます。すべてのテストファイルは testing サイドバーにロードされるため、各ファイルを画面上に展開したり、個別に記述ブロックを描写でき、各テストを実行、表示、監視、デバッグできます。
  • Playwright を TypeScript で実装すれば、すぐに動作し、優れた IDE との統合が可能です。IDEはあなたができることをすべて表示し、何か間違ったことをするとハイライトします。TypeScript の経験は必要ありません。コードが TypeScript である必要もありません。必要なのは拡張子.tsでテストを作成するだけです。

すべてのブラウザでテスト

Playwright では、プラットフォームを問わず、すべてのブラウザを使って簡単にテストできます。すべてのブラウザでテストすれば、すべてのユーザーの環境でアプリが動作することを保証できます。テストするブラウザの設定は、設定ファイルにプロジェクトの名前と使用するブラウザ、またはデバイスを追加して設定できます。

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

export default defineConfig({
  projects: [
    {
      name: 'chromium',
      use: { ...devices['Desktop Chrome'] },
    },
    {
      name: 'firefox',
      use: { ...devices['Desktop Firefox'] },
    },
    {
      name: 'webkit',
      use: { ...devices['Desktop Safari'] },
    },
  ],
});

Playwright の依存関係を最新に保つ

Playwright のバージョンを最新に保つことで、最新のブラウザバージョンでアプリをテストし、最新のブラウザバージョンが一般公開される前に不具合を検出することができます。

npm install -D @playwright/test@latest

リリースノートをチェックして、最新バージョンとリリースされた変更点を確認します。

npx playwright --version

CIでテストを実行する

CI/CD をセットアップし、テストを頻繁に実行しましょう。テストの実行頻度は多ければ多いほど良いです。コミットやプルリクエストのたびにテストを実行するのが理想的です。Playwright にはGitHubアクションのワークフローが付属しており、GitHubを利用するのであれば、設定不要でCI上でテストを実行できます。もちろん、Playwright をお好みのCI環境でセットアップもできます。

CI 上でテストを実行するときは、Linux を使ったほうが安上がりです。開発者は、ローカルでテスト実行するときはどんな環境でいいですが、CI上ではLinuxを使いましょう。

Lintを利用する

Lint を利用することで、エラーを早期に発見できます。@typescript-eslint/no-floating-promisesESLint ルールを使って、Playwright API の非同期呼び出しの前にawaitがないことを確認できます。

並列処理とシャーディングの使用

Playwright はデフォルトでテストを並列実行します。ひとつのファイル内のテストは、同じワーカープロセス内で順番に実行されます。ひとつのファイルに独立したテストが多数ある場合は、それらを並列に実行するとよいでしょう。

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

test.describe.configure({ mode: 'parallel' });

test('runs in parallel 1', async ({ page }) => { /* ... */ });
test('runs in parallel 2', async ({ page }) => { /* ... */ });

Playwright はテストスイートをシャードして、複数のマシンで実行できます。

npx playwright test --shard=1/3

生産性向上のヒント

ソフトアサーションを使う

テストが失敗した場合、Playwright はテストのどの部分が失敗したかを示すエラーメッセージを表示し、VS Code、ターミナル、HTML レポート、トレースビューアのいずれかで確認できます。また、ソフトアサーションも利用できます。これはテストの実行を即座に終了させるのではなく、テストが終了したら失敗したアサーションの一覧をコンパイルして表示します。

// Make a few checks that will not stop the test when failed...
await expect.soft(page.getByTestId('status')).toHaveText('Success');

// ... and continue the test to check more things.
await page.getByRole('link', { name: 'next page' }).click();

このベストプラクティスはPlaywrightに特化型のベストプラクティスだけど、E2E全体的なベストプラクティスを知りたい方は「E2Eテスト自動化を成功に導く10のベストプラクティス」も参考にどうぞ。

追記: Cypressのベストプラクティスも翻訳してみました。

“Playwrightのベストプラクティスを翻訳してみた” への 2 件のフィードバック

コメントを残す

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください