RevComm Tech Blog

コミュニケーションを再発明し 人が人を想う社会を創る

Playwrightを活用した本番稼働を支えるE2Eテスト実践のコツ

はじめに

RevCommのフロントエンドエンジニアの上川康太です。MiiTel Call Centerというプロダクトの開発を担当しています。

私たちは2023年の6月にMiiTel Call Centerを正式リリースしてから、スピード感を持って新機能の開発を進めてきました。

開発スピードを維持するためにも自動テストを増やして、デグレを防ぐことが重要だと考えています。そのため、PlaywrightによるE2Eテストを充実させてきました。

その中で得られたPlaywrightのコツについて共有したいと思います。

想定読者

  • Playwrightを使用したE2Eテストの作成に興味がある開発者
  • 実践的なテストコーディングのコツや、より効率的なデバッグ方法について学びたい方

Playwrightとは

PlaywrightとはE2Eテストを実行できるOSSのツールです。

複数のブラウザ(Chromium、WebKit、Firefoxなど)と複数のプラットフォーム(Windows、Linux、macOSなど)に対応しています。

ユーザーアクションを模倣するプログラムを簡単に作成できます。これによりページナビゲーション、要素検索、テキスト入力やクリック操作などを利用したテストシナリオを実行することが可能です。

また、自動待機、スクリーンショット取得や複数タブなどを利用でき機能が充実していることが特徴です。

ローカルでの開発のコツ

VS Code拡張機能でデバッグ

Playwright Test for VSCodeというVS Code拡張機能で、テスト実行時にTrace Viewerの起動をONにしておくとローカルでのデバッグが非常にやりやすくなるのでおすすめです。

VS CodeでShow trace viewerにチェックを入れると、テスト実行時にTrace Viewerが自動的に立ち上がります。

Trace Viewerでは、各アクションによる状態を確認する事ができます。画面のスナップショットや、コンソールのログ、ネットワークの状態を確認することで素早くデバッグを行う事ができます。

ローカルサーバーを別で立てる

playwright.config.tsのwebServerの設定で、Playwright実行時にローカルで立ち上がるサーバーのportを指定できます。普段の開発で使用しているportと異なるものを指定し、お互い干渉しないようにする事で、スムーズなテスト開発を実現できます。

/* Run your local dev server before starting the tests */
 webServer: {
   command: 'yarn dev:e2e',//E2E用のdevテナントを起動するコマンド
   port: 3333,//E2E用にポート番号を設定し、開発用のローカルサーバーと干渉しないようにする
   timeout: 120 * 1000,
   reuseExistingServer: !process.env.CI,
 },

CIでPlaywrightを実行する時のコツ

CI失敗時のレポートをコメント

通常、CIでE2Eテストが失敗した場合、ログは確認できますがどの画面で失敗したのかを確認できず、デバッグが難しくなります。

Playwrightの場合はHTML Reportを出力することができます。Call Centerのフロントエンドチームでは、E2Eテスト失敗時にレポートをS3にデプロイし、PRに対して自動的にURLをコメントするGitHub Actionsを作成しています。これにより素早いデバッグを実現しています。

社内のリポジトリで汎用的な reusable workflowsが管理されており、その中のS3にデプロイするworkflowを利用して実現しています。

timeout値を増やす

playwright.config.tsでテストのtimeoutの設定ができます。
ローカルでテストは全て成功するのに、GitHub Actionsでテストを実行した時にtimeoutで失敗する事が起きていました。

ローカル環境とGitHub Actionsのスペックの違いによる実行スピードの遅延が主な原因と考えられるため、timeout値を増やす事で対応しました。 ただし根本的な解決策ではないので、今後の高速化対応が必要だとは思います。

/* Maximum time one test can run for. */
 timeout: 120 * 1000,
 expect: {
   /**
    * Maximum time expect() should wait for the condition to be met.
    * For example in `await expect(locator).toHaveText();`
    */
   timeout: 60 * 1000,
 },

Playwrightでテストコードを書く時のコツ

getBy〇〇を使う

基本的に要素を指定する際にLocatorsのgetBy〇〇を使用するのが推奨されています。これらを使用することで、ユーザーの使い方にできるだけ近い形のテストが実現できます。また、可読性の高いテストコードとなります。

button、a、inputなどの要素を見つけるには、getByRoleが使用できます。

await page.getByRole('button', { name: 'Save' }).click();

div、span、pなどの要素を見つけるには、getByTextが使用できます。

await expect(page.getByText('user name')).toBeVisible();

iconのsvgにaria-labelが付与されている場合などはgetByLabelで指定できます。

await expect(page.getByLabel('icon-label')).toBeVisible();

他のロケーターが使用できない場合は、getByTestIdを使用します。ただし、実際にユーザーはtestIdを見ることはできないため、上記のロケーターを使用する事が推奨されます。

await expect(page.getByTestId('test-id')).toBeVisible();

test.stepを使う

test.stepを使用すると細かい単位でテストに名前をつけて可読性を上げる事ができます。

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

test('テスト', async () => {
  await test.step('ログイン', async () => {
    // ...
  });

  await test.step('データを作成する', async () => {
    // ...
    expect(true).toBe(false);
  });

  await test.step('ログアウト', async () => {
    // ...
  });
});

さらに、名前はレポートに表示され、デバッグ時にどの段階で失敗したかが分かりやすくなるのでおすすめです。

効果的なE2Eテストを書くコツ

デグレが発生しやすいケースをテスト

チームでは、E2Eテストの基本方針として、実行時間、コストを考慮し、各機能に対してハッピーパス(正常系の基本的な使用ケース)のテストを書くこととしています。ここに加えて、確認が漏れやすいケースや、実際にデグレが発生しやすいケースに対して、E2Eテストを書くことが効果的だと感じています。

例えば、影響範囲の多い共通のコンポーネントを修正した際に、意図しない画面スクロールが発生してしまうケースがありました。

対策として、下記の関数を作成して、各ページで呼び出しています。意図しない画面スクロールが発生した場合は、テストが失敗して検知できるようになりました。

export const expectNotScrollablePage = async (page: Page) => {
 // ページの高さを取得
 const pageHeight = await page.evaluate(() => document.documentElement.scrollHeight);
 // ビューポートの高さを取得
 const viewportHeight = await page.evaluate(() => window.innerHeight);
 // ページの高さがビューポートの高さと一致している (スクロールバーが表示されない) ことを確認
 expect(pageHeight, 'should be not scrollable page').toBe(viewportHeight);
};

こういう影響範囲が多く、確認が漏れやすい部分に対しての1つの打ち手としてE2Eテストを整備する事が効果的だと感じました。

おわりに

以上のコツを活用することで、PlaywrightによるE2Eテスト開発が少しでも楽になれば嬉しいです。

今後の課題としては、Playwrightの実行時間が長くなっていたり実行結果が不安定な部分もあったりするので、それらを解決していきたいです。