その「たまに落ちるテスト」、お前のコードが腐っている証拠だ。
「昨日は通ったはずのCIが、今日は赤色に染まっている」。
この地獄のような通知に、お前はあと何回付き合うつもりだ?
「GitHub Actionsの環境が悪い」「ネットワークが重い」。そうやって環境のせいにしている間にも、お前のプロダクトの信頼性は確実に削り取られている。テストが「たまに落ちる」状態は、もはやテストではない。ただのノイズだ。
俺もかつては同じ沼にいた。だが、Playwrightの挙動を理解し、CIの限界を知った今なら言える。「Flakyなテスト」を放置するのは、メンテナンスを放棄しているのと同じだ。 今日でその負のループを断ち切るぞ。
1. 「固定待機」は悪魔の所業だと知れ
`page.waitForTimeout(3000)`。この一行を書いた瞬間、お前のテストは「運ゲー」に成り下がる。
なぜダメなのか? ブラウザのイベントループや、DOMのレンダリング完了タイミングは、お前のPCとCI環境で全く異なるからだ。3秒待っても終わらない重い処理が走った瞬間、テストは死ぬ。
正しいのは「状態を待つ」ことだ。PlaywrightのAuto-waitingは、DOMがアタッチされ、可視状態になり、アクション可能になるまでを自動で監視してくれる。
// 悪い例:固定待機。これ書いた時点で運任せのギャンブル
await page.waitForTimeout(3000);
await page.click('#submit-button');
// 良い例:状態を待つ。Playwrightの強力なWeb First Assertionsを信じろ
const submitButton = page.locator('#submit-button');
await expect(submitButton).toBeVisible();
await submitButton.click();
もし要素が非同期で動的に生成されるなら、`expect.poll`でポーリングを行うか、`locator`のタイムアウトを調整して「確実な状態」を待つのがプロの作法だ。
2. 認証のたびにログインAPIを叩くな
テストごとにログイン処理を走らせる? 愚かだ。DBにゴミが溜まるし、レートリミットで自爆する。俺はこれで、個人開発のサービスを自らダウンさせたことがある。
`storageState`こそが正義だ。 ログイン後のCookieを保存し、それを使い回す。これだけでテストの実行時間は劇的に短縮され、CIの安定性は跳ね上がる。
推奨されるパイプライン構成
GitHub Actionsで認証情報を扱うなら、シークレットにJSONを直書きして環境変数でパースするのはやめろ。エスケープ漏れで泣くことになる。`actions/upload-artifact`でファイルをやり取りするのが、最も堅牢でモダンな手法だ。# .github/workflows/test.yml
jobs:
auth:
runs-on: ubuntu-latest
steps:
- run: npx playwright codegen --save-storage=auth.json
- uses: actions/upload-artifact@v4
with:
name: auth-data
path: auth.json
test:
needs: auth
runs-on: ubuntu-latest
steps:
- uses: actions/download-artifact@v4
with:
name: auth-data
- run: npx playwright test --storage-state=auth.json
3. リソース制限という「見えない壁」を突破せよ
フルスタックアプリを並列実行(`workers: 100%`)させて、CIがメモリ不足で落ちる経験は誰しも通る道だ。GitHub Actionsの無料枠(2コア・7GBメモリ)でブラウザを大量に回せば、そりゃあ悲鳴も上げる。
単なる制限から「分散実行(Sharding)」へ
単純に`workers`を絞るのも手だが、プロジェクトが巨大化すればそれでは間に合わない。`--shard`オプションを使って、テストを物理的に分割し、複数のRunnerで並列実行しろ。// playwright.config.ts
export default defineConfig({
// CI環境ではメモリ負荷を考慮してワーカー数を制限しつつ、再試行で安定性を担保
workers: process.env.CI ? 2 : undefined,
retries: process.env.CI ? 2 : 0,
});
CIの設定で`npx playwright test --shard=1/3`のように実行単位を分ければ、どんなにテストが増えても実行時間は一定に保てる。これこそがスケールするCIの姿だ。
最後に:テストは「守り」ではなく「武器」だ
テストを書く目的は「バグを見つけること」だけじゃない。「自信を持ってデプロイし続けること」だ。CIが落ちるたびに「またか」と溜息をついているようでは、開発者は新しい機能を作る意欲を失う。
- 固定待機を捨て、状態を待て。
- 認証は使い回せ。
- CIのリソースを理解し、分散実行で最適化しろ。
さあ、その腐ったテストコードを今すぐリファクタリングしろ。