🧪

【Playwright】コイツに気をつけろ!6選

2024/12/24に公開

はじめに

「操作順は完璧、Debug での動作確認も OK だけど... コマンド実行では失敗してしまう」

「Local でコマンド実行 OK だけど... GitHub Actions での実行で失敗してしまう」

「成功率・失敗率が半々...」

などなど...

私自身、Cypressでの E2E や、Jest + RTLでユニットテストを組んでいたこともあり、Playwrightも大丈夫だろうと思っていましたが、一筋縄ではいかないケースが結構ありましたので、まとめたいと思います!

対象読者

  • Playwrightを使い始めようとしている人

  • 調査で手詰まりになってどうしようも無い人

これからご紹介する点を、何となく頭の片隅に入れておくことで、スムーズなPlaywrightライフが送れるはずです!

設定編(playwright.config.ts

timeOut

Timeout for each test
テストごとのタイムアウト時間

test()で括られる範囲に対するタイムアウト時間を設定するもので、長すぎると、例えばwaitFor()の待機処理で延々待ってしまいテスト失敗の検知が遅れたり、短すぎるとテストステップが完了する前にタイムアウトエラーになってしまいます。

冒頭で書いた不安定なテスト結果を招いていたのは、後者が原因で、気づくまでに結構時間がかかりました。

というのも、「たまに失敗する」「Local 実行では問題ない」といった感じで絶妙に他の原因を探ってしまいそうな状況だったためです。

最終的には...

Test timeout of XXXXXms exceeded.エラーが、エラーが起こり得るはずのないawait page.waitForTimeout(xxxx);という単純な時間待機処理で発生していたケースを発見し、タイムアウト時間短すぎ問題に気づくことができました!

ちなみに、既定値は30秒ですので、テスト成功率が不安定な場合は、まずこの設定を確認すると良さそうです!

workers

Defaults to half of the number of logical CPU cores.
論理 CPU の半分が既定値

既定が並列処理であるPlaywrightだが、どうも GitHub Actions での実行が並列になってなさそうと報告を受け調査した内容です。

これは GitHub Actions の仮想マシンの CPU コア数から、Worker が1つしか割り当てられていないことが原因でした。

ちなみに、reporterに、["list"]を設定していれば、テスト実行時に、使用する Worker 数がターミナルに表示されます。

playwright.config.ts
export default defineConfig({
  reporter: [["list"]],
});
$ npx playwright test
Running 1 test using 1 worker

fullyParallel

By default, test files are run in parallel. Tests in a single file are run in order, in the same worker process.
You can configure entire test project to concurrently run all tests in all files using this option.
既定として、テストファイルはそれぞれ並列で処理され、1つのテストファイル内のテストは同じ Worker 内で順番に実行される。
この設定を通して、全てのテストファイルの全てのテストを並列実行することができる。

同一ファイル内のテストも並列実行したい場合に使う設定です。

当初、ドキュメントをちゃんと読んでおらず、とりあえず並列処理を有効にしてくれそうだったので ON にしていましたが、全く不要でした...

workersと合わせて、並列実行に関する設定群として覚えておきたいですね。

実装編

待機処理

<a href="/login">のように、link で画面遷移を行う処理において、以下のような実装を行うとします。

await page.getByRole("link", { name: "Loginページへ" }).click();

// この処理がLoginページへ遷移する前に走ってしまう
await page.getByPlaceholder("sample@sample.com").fill("test@gmail.com");

一見、特に問題なさそうで、「Login ページに遷移して、メールアドレス入力を行う」という操作を完遂してくれそうに思いますが...

実行環境の性能によっては、メールアドレス入力の操作が、Login ページへの遷移完了前に実行されてしまい、失敗する場合があります。

こういったケースでは、以下のように待機処理を入れてあげる必要があります。

await page.getByRole("link", { name: "Loginページへ" }).click();

// Loginページに遷移したことが確認できる内容で待機を入れる
// await page.waitForURL('**/login');などでもOKです。
await page.getByText("メールアドレスを入力してください。").waitFor();

// この処理がLoginページ遷移後に実行されることが保証できる
await page.getByPlaceholder("sample@sample.com").fill("test@gmail.com");

Browser での描画が主なアプリケーションにおいては、この待機処理がテスト実行安定度の明暗を分けます。

Playwright に限らず E2E テストにおいて、待機ステップを制するものが、E2E テスト を制すと言っても過言ではありません。

ページアクセスエラー

何の変哲もない以下のページアクセス処理にて、A is interrupted by another navigation to Bというエラーが発生する場合があります。

await page.goto("/account");

このエラーは、上記処理の最中に、別 URL への遷移が同時に実行されることが原因で発生します。

例を挙げると、例えばReactuseEffectフック内で画面遷移を入れていると、本ケースのように衝突してしまいますね。

対策としては、以下のように待機処理を入れることになります。

// 完全に前処理のURL書き換えが終わるまで待機する
await page.waitForURL("**/sign_out");

await page.goto("/account");

おまけ(MailSlurp)

Playwrightと連携させているMailSlurpというサービスに関して、ハマったケースがあったので紹介させてください。 (MailSlurpの詳細はこちら)

MailSlurpはテスト用メールアドレスや受信箱などを提供してくれるサービスで、PlaywrightからMailSlurp APIを使って受信箱にアクセスしたりしています。

特定の受信箱から最新のメールを取得する処理は以下のようになります。

const getLatestEmail = async (inboxId: string) => {
  const mailslurp = new MailSlurp({ apiKey: "apiKey" });
  return await mailslurp.waitForLatestEmail(inboxId);
};

const latestEmail = await getLatestEmail("inboxId");

ただなぜか分からないのですが、あるタイミングから最新メールが取得できなりました。 しかも取得されるメールの規則性がよく分からない...

結構細かくケースを整理して、サポートに問い合わせてもドキュメントを共有されるだけ。理由は「今めっちゃ忙しいから」とのこと...

結局、原因不明のままなのですが、以下のように対処できました。

const getLatestEmail = async (inboxId: string) => {
  const waitForLatestEmailRequest: WaitForLatestEmailRequest = {
    inboxId,
    sort: WaitForLatestEmailSortEnum.ASC,
    since: new Date(Date.now() - 10 * 60 * 1000),
  };
  return await mailslurp.waitController.waitForLatestEmail(
    waitForLatestEmailRequest
  );
};

終わりに

雑多な内容になりましたが、実際にPlaywright実装を進めている中でハマったケース達です。

特にtimeOutに関しては、本当に悩まされたので注意が必要です...(Playwrightの話ではないですが、AWS Lambdaやその他のサービスでも Timeout に苦しめられた記憶があります)

ということで、読んでくださった方のPlaywrightライフの一助になれば幸いです!

Linc'well, inc.

Discussion