ランサーズでEMをしている成田(@narinarinari)です。
先月、PHPを安全にバージョンアップさせるために、PlaywrightでE2Eテストを導入しました。PlaywrightのUIモードで実行結果を表示した時、「すげー!」と興奮したのが既に懐かしいです。
コードを書いていく中で、Playwrightは「Wait」系APIが非常に肝だと感じました。悪戦苦闘する中で得た知見を、ここに記していきたいと思います。
page.waitForTimeout() を使わない
これは1秒待機する書き方です。
// wait for 1 second
await page.waitForTimeout(1000);
公式ドキュメントでも記されていますが、あくまでデバッグ用に使いましょう、とあります。本番利用・安定動作を目的としたE2Eテストには推奨されていません。
「とりあえず形だけ作るか」と勢いでコードを書いていた時に、実行環境やタイミングのズレでテストが不安定になり、この「Wait」系APIの奥深さを知るきっかけになりました。
page.waitForURL() でページ遷移完了を待つ
ページ遷移後に操作するテストを書く際はこのAPIを使いましょう。
ページ遷移前に対象の操作が実行されてしまい、次の操作が出来ずタイムアウトしてしまう経験は非常に多いと思います。
await page.click('a.delayed-navigation'); // Clicking the link will indirectly cause a navigation
await page.waitForURL('**/target.html');
page.waitForResponse() でAPI実行完了を待つ
外部AIのAPI実行完了待ち、外部決済のAPI実行完了待ち、さまざまなケースでこれを使っています。API連携を主とするサービスでE2Eテストを書くときは必須になるかと思います。
const apiUrl = 'https://example.com/api/users';
const responsePromise = page.waitForResponse(response =>
response.url() === apiUrl && response.status() === 200
&& response.request().method() === 'GET'
);
await page.getByText('ユーザー一覧取得').click();
const response = await responsePromise;
Locator.waitFor() でスピナー表示が消えるのを待つ
APIの実行完了待ちだけでは表現しきれず、スピナー表示などが間に挟まる場合、このテクニックがとても有効です。
const spinner = page.locator('.loading-spinner');
// スピナーがDOMから完全に消えるのを待つ
await spinner.waitFor({ state: 'detached' });
上記のやり方に気付く前は、非推奨の書き方で一時的にやり過ごしていましたが、こちらも非常に不安定だったので悩みの種でした。
// ネットワークアイドルまで待つ(非推奨)
await page.waitForLoadState('networkidle');
最後に
2ヶ月かけて気合いで大半のケースを書き切りましたが、まだまだ課題が多いです。開発スピードを損ねず、適切な粒度でのテストはどこまで書くべきか。そして何よりE2Eテストは実行時間が掛かります。この実行時間を短縮するためのTipsもいずれ書いていこうと思います。
isanaさんが書いた記事、とても素敵な内容だったので載せておきます。
それではまた〜。