AI駆動開発でE2Eテストを並列化するときに気をつけたいこと
AI駆動開発をしていると、開発そのものがどんどん並列になっていきます。
複数のAIエージェントが別々のブランチやworktreeで実装し、それぞれがテストを実行する。さらに1つのE2Eテストの中でも、Playwrightが複数ワーカーを使ってテストを並列実行する。
こうなると、以前なら
「E2Eテストが落ちた = コードに問題がある」
と考えればよかったものが、少し違ってきます。
AI駆動開発では、
「単に同時に動かしすぎているだけ」
というケースが増えてくるからです。
今回は、Playwrightを使ってE2Eテストを並列実行するときに知っておきたいポイントを、なるべくシンプルに整理します。
並列化すればするほど速くなる、とは限らない
PlaywrightにはE2Eテストを並列実行する仕組みがあります。
現在のPlaywright Testでは、特に指定しなければ論理CPUコア数の半分がワーカー数として使われます。たとえば12論理コアなら、おおむね6ワーカーです。
もちろん並列化すればテストは速くなります。
ただし、問題はE2Eテストがかなり重いことです。
E2Eでは単にJavaScriptを実行しているわけではありません。
- ブラウザ
- Webアプリ
- APIサーバー
- DB
- キャッシュ
- Docker
- ビルド処理
などが同時に動いています。
さらにAI駆動開発では、別のAIエージェントも同じPC上でビルドやテストをしている可能性があります。
つまり、
Playwrightのワーカー数だけを見ても、実際の負荷は分からない
ということです。
よくあるのが「謎のエラーが一気に増える」現象
並列度を上げすぎると、面白いことに同じエラーだけが発生するとは限りません。
たとえば、
- 画面遷移がタイムアウトする
- ブラウザが突然終了する
- APIが401を返す
- 作成したはずのデータが見つからない
- 件数が合わない
- 普段成功しているログインが失敗する
など、まったく違うエラーが同時に現れることがあります。
これを見ると、
「6個のテストが壊れた」
と思ってしまいます。
ところが実際には、
マシンに負荷をかけすぎたという1つの原因から、6種類の症状が出ている
だけかもしれません。
ここが並列E2Eの難しいところです。
まず workers=1 で確認する
そこで覚えておきたい、かなりシンプルな切り分け方法があります。
E2Eで不可解なエラーが複数出たら、まず並列実行をやめます。
Playwrightなら、
npx playwright test --workers=1
です。
これでテストが全部通ったなら、
コードそのものより、並列実行による負荷やテスト同士の干渉を疑う
ことができます。
逆に、workers=1でも同じテストが落ちるなら、そのテストや実装自体に問題がある可能性が高くなります。
個人的には、並列E2Eで問題が発生したとき、
並列度を1に落として再現するか確認する
を最初の切り分けにしてしまうのがよいと思っています。
いきなりタイムアウト時間を伸ばしたり、リトライを増やしたりするより、はるかに原因を追いやすくなります。
ブラウザが分かれても、システム全体が分かれるわけではない
もう1つ重要なのが、テストの独立性です。
Playwrightは各テストをBrowserContextで分離してくれます。
しかし、
バックエンドまで自動的に分離してくれるわけではありません。
たとえば複数のテストが、
- 同じユーザー
- 同じ会社・テナント
- 同じDBレコード
- 同じメールアドレス
- 同じポート
などを使っていたら、互いに影響する可能性があります。
Playwrightの公式ドキュメントでも、サーバー側の状態を書き換えるテストを並列実行する場合には、ワーカーごとに別アカウントを持たせる方式が推奨されています。
たとえば、
「ユーザーの設定を変更するテスト」
と
「ユーザーの設定が初期値であることを確認するテスト」
を同じユーザーで同時に実行すれば、当然ぶつかります。
これはPlaywrightの問題ではありません。
テストデータの設計の問題です。
AI駆動開発では「二重の並列化」に注意する
ここは従来の開発以上に重要だと思っています。
たとえば、AIエージェントを4つ並列で動かしていたとします。
そして、それぞれがPlaywrightを6ワーカーで実行したらどうなるでしょう。
単純化すると、
4エージェント × 6ワーカー = 最大24系統
のE2E処理が同じマシン上で動く可能性があります。
もちろん実際には常に24ブラウザが完全同時に動くわけではありません。
ただ、
「Playwrightは6ワーカーだから大丈夫」
とは言えなくなります。
AI駆動開発では、
エージェントの並列数 × テストランナーの並列数
まで考える必要があります。
Git worktreeなどを使えばソースコードはきれいに分離できます。
しかし、
CPUやメモリまでworktreeごとに分離されるわけではありません。
ここは意外と見落としやすいポイントです。
「速くする」より「安定して再現できる」を優先する
E2Eテストでは、最高速度を狙う必要はあまりありません。
たとえば、
export default defineConfig({
workers: 3,
});
のように、安定するワーカー数を明示してしまう方法もあります。
Playwrightはプロジェクト単位でもワーカー数を制限できるため、共有リソースを触るテスト群だけworkers: 1にすることもできます。
2ワーカーなら安定して10分。
6ワーカーなら7分だけれど、5回に1回失敗する。
この場合、後者のほうが速そうに見えて、実際の開発効率は悪くなります。
AIが失敗ログを読み、
原因を調べ、
コードを変更し、
もう一度テストする。
という無駄なループまで始まるからです。
不安定なテストは、人間だけでなくAIの時間も消費します。
リトライで緑にして終わらせない
Playwrightにはリトライ機能があります。
便利な機能ですが、並列実行による問題をリトライで隠してしまうことには注意が必要です。
1回目。
失敗。
2回目。
成功。
結果はGREEN。
一見すると問題ありません。
しかし、本来確認すべきなのは、
なぜ1回目に失敗したのか
です。
負荷やデータ競合が原因なら、その問題は残ったままです。
AIにとっても「最終的に成功した」という結果だけを見せるより、失敗時のtraceやログを残して原因を調査できる状態にしておくほうが有益です。
Playwrightではretain-on-failureなど、失敗時にtraceを残す実行方法も用意されています。
ローカルのE2Eサーバーにも気をつける
E2Eテストでは、PlaywrightからローカルのWebサーバーを起動する構成もよく使います。
PlaywrightにはwebServerという仕組みがあり、テスト開始前にアプリケーションサーバーを起動できます。
ここでも並列開発では注意が必要です。
たとえば、
- worktree A
- worktree B
- worktree C
が全部、
localhost:3000
を使おうとしたら当然ぶつかります。
さらに既に起動しているサーバーを再利用する設定では、
別のworktreeで起動したサーバーに対してテストしていた
ということも起こり得ます。
「コードを直したのにE2Eでは直っていない」
と思ったら、
実は別ブランチのサーバーを見ていた。
AI並列開発では、こうした「コード以外の共有資源」にも目を向ける必要があります。
並列E2Eで問題が出たときの考え方
細かいテクニックはいろいろありますが、まずは次の順序だけ覚えておけば十分だと思います。
- workers=1で実行する
- それでも落ちるなら、コードやテストを調べる
- 通るなら、並列干渉やマシン負荷を疑う
- workersを2、3、4と少しずつ増やす
- 安定する範囲を標準値にする
- 同じユーザーやDBデータを共有していないか確認する
重要なのは、
「E2Eが落ちたからテストコードを直す」から始めないこと
です。
AI駆動開発では「実行環境」も設計対象になる
AI駆動開発によって、コードを書く速度はかなり上がりました。
その結果、次にボトルネックになってくるのが、
ビルド、テスト、Docker、DB、ブラウザといった実行環境
です。
これまで1人の開発者が順番に実行していた処理を、複数のAIが同時に実行するようになる。
するとCPUやメモリ、ポート、DB、テストアカウントといった、
これまでそれほど意識しなくてもよかった共有資源が問題になります。
Playwrightのworkersは単なる高速化設定ではありません。
限られたマシン資源を、何本のE2Eテストに同時配分するかを決める設定
と考えたほうが分かりやすいでしょう。
そして並列E2Eで不可解なエラーに遭遇したら、最初にやることは難しくありません。
npx playwright test --workers=1
まず1本で走らせる。
それで消えるエラーなら、コードを直す前に並列実行の仕組みを疑う。
AIが大量のコードとテストを高速に生成する時代だからこそ、
「たくさん並列に動かせること」と「たくさん並列に動かしてよいこと」は別
という感覚が、これからのAI駆動開発では重要になってくると思います。