この記事を書くことになったきっかけ
銀の滴降る降るまわりに。メリークリスマス!🎄
「Qiitaのアドベントカレンダーは今年も賑わっているなあ…最近やってなかったし今年はひさびさいっちょ噛みするか」 とカレンダー一覧を眺めていたら、カレンダーのお題に全然関係ない記事が上がりまくっている豪華プレゼント付きカレンダーを見かけてしまい、面白すぎたので参加させていただくことにしました。
ライバルが少ないぞシメシメと最優秀賞のiPadを狙っているといったよこしまな気持ちは一切ありません。優秀賞のAmazonギフト券3万円分がほしいという純粋にピュアな金銭欲でございます。問題ございましたらこの記事の参加は即刻取り下げますので垢BANはやめてくださいお願いします。
本当は本当にTricentisを試したかったのですが
フリーで14日間お試し使用できるらしいのですが…
- そのためにまず氏名や会社名等をフォームから入力しないといけない
- フォームで入力したらあとは自由にダウンロードできるという状態になるのかもわからない(営業さんが接触してくるだけかもしれない可能性)
- 価格設定が非公開
つまり:めんどくさい
という、高度に複雑な理由のため極めて遺憾ながら断念するに至りました。
でも自動テストツールには興味があります本当です
ここからはちょっと真面目に書きます。
今回Tricentisに興味を持ったのは何もプレゼント目当てだけではなく「業務アプリの E2E テスト」に以前より継続的な課題感を抱いていたためです。
GUI でポチポチするだけでシナリオが組めて、画面遷移のチェックもしてくれる――システム開発・運用界隈で昔から囁かれる、美しい夢。その夢を追い求め現れては消えていく、泡沫のようなテストサービスたち。
自分でもSeleniumなどを使い似たようなアプローチを試したことが何度かありましたが、結論は「一時的に特定のケースだけを繰り返し実行するテストはGUIで十分な場合もあるけれど、ケースの網羅と安定稼働を考えるなら、結局テストコードをゴリゴリ書いた方が早い」でした。Seleniumでは画面操作をコードとして記録することで繰り返しのテストが可能ですが、基本の操作を記録した上でバリエーションの網羅を行うためにいちいち微妙に異なる操作を記録させるのはあまりに効率が悪く、結局コードで書いた方が早い、になるんですよね。
しかし、非常に残念なことに、そのようにテストコードをゴリゴリ書けるエンジニアは、テストよりも機能開発の方にリソース取られがちです。
となればE2Eテストはというと、中途半端なテストコードよりも人の目の方が信用できると、人海戦術で解決されがちです。
とはいえ人力で全ての機能を確認し続けるのはなかなか難しく、度重なる「追加機能部分のみの動作確認」により、デグレが発生しがちです。
それがこれまでのシステム運用界隈の常識だったのではないでしょうか。
いや仕方ないですよね。軽微な変更ごとの全機能人力リグレッションテストとか、自分もやりたくないですよ絶対。
しかし世は大AI時代。人間がやる余裕がなくてもAIならやれるんじゃないですかぁ? と。
そして、AIって良くも悪くも結果に揺らぎがあるじゃないですか。ノウハウの蓄積という意味ではE2EテストのコードをAIに書いてもらうのがベストな戦略なんじゃないですかぁ? と。
そもそも、AIエージェントにコード実装を任せるにあたり、テストの実装は非常に適した分野と考えています。AIに機能実装を自走する形で任せると良くも悪くも多大な気遣いと共にいらん機能を勝手に開発しようとしてくることがあるのですが、すでに動いているコードに対する「テスト実装」を任せると、レアケースのテストやパターン網羅を嫌がらずに細々と書いてくれる、非常に頼になる存在です。
そんなわけで最近はユニットテストは大半をAIに書いてもらっていたのですが、ユニットテストが書けるのならE2Eテストだって書けるのでは? と考えるのは自然な流れですよね。
今回やったこと
E2Eテストに求められがちな内容
1、「どういう確認でテスト結果をOKとしたのか、エビデンスを残してほしい」
2、「仕様がわからないから、仕様を調べながらテスト計画立ててほしい」
以上2点を「AIにE2Eテストを実装してもらう」という方法で叶えられるのか、ということを、実際にやってみて検証してみました。
具体的な手順
(だらだら開発日誌みたいになったので、面倒な方は次の項目へ)
DAY-1. テスト対象を用意する
テストをするにあたって必ず必要なもの。それはテストをする対象です。
テスト作るのを試してみるぞー、という気持ちでいるとうっかり「テストしやすいシステム」を用意してしまう懸念がありましたので、今回は自分のリポジトリにもともと転がっていた TAP DASH HERO というブラウザゲームをテスト対象とすることにしました。
こんなゲームです。
ご参考までにいちおうこのゲームのソースコードも公開しておきます。
今回の記事向けに新たに用意したリポジトリですが、ゲーム本体機能に関係のないファイルの削除と、ライセンス情報の追加(音声や一部の画像はフリー素材を使っているのでそこの制限を明確に)、そしてテスト環境として繋ぎこむためのドメイン・ネットワークまわりを少しいじった以外は、ゲームそのものは変更していません。元はClineの機能検証のためにゲームを作ろうとしていた…ようでした。(おぼえてない)
今回のために「テストをしやすくするための追加機能の開発」は行わないことを前提としました。
ゲームのE2Eテストは実装したことがないので自前のノウハウは基本役に立ちません。
E2Eテストがよく書かれているタイプのシステムではありませんし、世間に知られているシステムでももちろんありませんので、実装を担当するAI側からしてもゼロショットのアプローチであることが期待されます。
さてどうなるでしょう。
DAY-2. テスト基盤を用意する
適当に実装勧めていると、チャットの窓を新しくした際にAIがこれまでと違うライブラリ使い始めてメチャクチャになる、という経験をしたことがあります。まずは基盤を整えます。
最近は、ゼロからの実装の際にはClaude Codeをよく使っており、今回もまずはClaude Codeを使いました。
起動して「localhost:8080で動いているTAP DASH HEROというウェブゲームのE2Eテストを実装するリポジトリを作成したい。まずは基盤から作成したいので、方針を提案して」的なことを入力します。
以前は、方針はまずその時点で「最もIQが高い」と評判の最新ハイエンドモデル(Geminiなど)に相談し、実装にはClaudeシリーズを使う、という役割分担でやっていました。しかし、 「最終的に担当する相手にはじめから相談する」というのがAIにおいても結局最適解 な気がしています。
そんなわけでClaudeから返事です。Selenium と Playwright を候補として挙げた上でPlaywrightを勧めてきましたので、それでやってもらいます。Playwrightは知らなかったのですが、担当者が良いとって言っているのはそれなりに理由があることなのでしょう。AIを信じて、基本そのままやってもらいます。
なお実装してもらっている間に一応自分でも情報収集しましたが、PlaywrightにはAuto-waitingがあるのとheadless時の速度が速いそうで、それはとてもいいなと思いました。
ちなみにAIに実装を頼む時に唯一毎度面倒に感じるのが、基本的にDockerなどのコンテナ技術を使ってくれないことです。ホストマシンにあれこれ入っている状態が好きではないので、今回もDokcerベースで動かすように変更してもらいました。技術的な指示を出したのはそれくらいです。
なお「E2Eテストに求められがちな内容」の 1、「どういう確認でテスト結果をOKとしたのか、エビデンスを残してほしい」 を実現するために、テストの確認結果を画面キャプチャとして残す、ということを後で依頼しようとしていたのですが、何も言わなくてもこの時点で画面キャプチャ機能を実装してくれていました。もー。好き!
DAY-3. テスト基盤の動作確認
テスト基盤を作って、と言っただけなんですが、ついでに基本的なテストも実装してくれました。AIは優秀ですね。
しかし動かしてみると、全てエラーになります。ホストマシンの ポート8080 で動いているはずのゲームにそもそもアクセスできていないようです。その旨をClaude Codeに相談すると、ゲーム側で設定変更するべき内容を教えてもらいました。適用すると動くようになりました。
そして次の問題が起こります。動くようにはなったのですが、実装済みのあらゆるテストが成功の扱いになっているもののテスト結果に保存されている画面キャプチャを見ると明らかにおかしいです。「ゲーム終了」という扱いになっているものが、ゲームオーバーではなくタイトル画面のままだったりします。これはまともにテストができていないようです。AIにあれこれ直してもらいましたが、同じような指摘を繰り返しているうちに修正がループしたり、待機時間が長いテストが混じっているのもあって、画面が固まるようになりましたw
自分でも結果の画面キャプチャを眺めていると、そもそも「プレイボタンを押したらゲームプレイ画面が表示される」というテストの画面キャプチャの内容がおかしいことに気づきました。ゲームプレイ画面が表示されていないのにゲーム終了のテストはできません。それを指摘すると、「いい指摘ありがとうございます!」と嬉々として?直してくれた上、最終的にも改めて「原因を教えていただいてありがとうございました!」って言ってました。直したの全部AI自身なのにこの低姿勢。こんな部下がいたら可愛がるしかないじゃん。自分の若い時代を少し反省しました。可愛げのない部下ですまんかったよあの頃の上司くん(たち)。
DAY-4. テスト実装計画を立案
基本的なテストは動くようになりました。さて、では、次に「どういうテストを実装するべきか」を考える必要があります。
「E2Eテストに求められがちな内容」の 2、「仕様がわからないから、仕様を調べながらテスト計画立ててほしい」 ですね。
はっきり言えばAIに頼む際にはこの手のプロセスが一番気楽です。引き続きClaude Codeに頼んだところ、TEST_PLAN.mdというファイルに、ここまでに実装済みのテストと、これから実装するべきテストをまとめてくれました。さんくす。
DAY-5. ランダム要素に対するテストを実装
前項でAIが立案したテストファイルの中身を見てみると、優先度「高」に以下の実装が挙げられていました。
- [ ] 敵を倒すとスコアが増加する
- [ ] 敵に当たるとゲームオーバーになる
- [ ] 複数の敵を連続で倒す(コンボ)
- [ ] 異なる種類の敵の挙動確認
TAP DASH HEROはアクションゲームです。画面の端からランダムに出現する敵に対し、自分から体当たりをすることでスコアを稼ぎます。一方で、敵の方からぶつかられるとゲームオーバーになります。
敵を倒せること、敵からやられること。この2点を外しては、E2Eテストの意味がありません。AIの判断は非常に的確と言えます。
しかし敵はランダムに出てきます。それを倒したり倒さなかったりする仕組みを、どうやってテストに組み込めばいいのでしょうか。
ゲーム本体の方で「敵の出現パターンを固定化するモードをつける」ことはできます。しかし、今回のアプローチの「テストのための機能開発はしない」という前提に抵触しますし、そもそもやりたくありません。「通常モードでのみ発生するバグ」を見逃す懸念がありますし、何より開発するのも管理するのもめんどくさいです。
そこでAIに「"ランダムに出てくる敵の存在を検知してそちらに向かってタップする"という挙動のコンポーネント作成」を依頼しました。
いきなりテスト実装を依頼しなかったのは、この挙動を実現できないままテストの実装をさせるとAIがパニックを起こすことを懸念し、手順を分けて依頼したのです。
それを具体的にどう実現するのかは自分の中では完全にノーアイデアでしたが、AIがうまく実現してくれました。気になる方はソースコードを読んでいただければと思いますが、ページの要素を取得し、要素名などから敵を判定の上、一番近い敵の位置の方向に移動する、ということをしているようです。この辺りの仕様は実際の挙動を調査しながらサクサク決めていました。かしこい。
DAY-6. 動作確認、テストバグの修正
ランダムに出てくる敵を倒す、というテストの実装を終えて改めて一通りテストを回してみたところ、2件ほどエラーが出ました。
この辺りで、相方をClaude CodeからGithub Copilotにチェンジしました。Claude Codeに不満があったわけではなく、単にうっかりです。
便宜上、この記事ではDAY-Xとステップ分けして書いていますが、実際にはDAY-5までは1日で一気にやりました。で、そこで一旦力つき、1日空けてDAY-6から再開、という感じだったので、その前まで何をしていたか詳しく忘れてしまっていだけです。
出来上がっているシステムを調査しながらちょこちょこ直すのはGUIのインターフェースの便利さもあってCopilotが頭抜けていると思っていますが、これくらいの内容であればずっとClaude Codeでも全然できたかなと思います。どのみち中の人(モデル)はClaude Sonnetシリーズですし。
ともあれ、ここからはGitHub Copilotなんだなあと思いながらお読みください。
というわけでGitHub Copilot。一旦はAskモードでCopilotにバグを調査してもらいます。すると、対象のテストでやたらと長時間のwaitが入っているのが原因とのことでしたので、Agentモードに変更して修正してもらいました。
このあたりの問題は、基本のテスト実装でエラーが出た際に、原因がはっきりしないままAIにあれこれ直しているうちに「プレイ画面が表示されないのはwait時間が足りないからでは」という仮定で入れたコードが残ったままになっており(なお仮定立案も実装もAIです)、その残存コードが後続の実装判断にも悪影響を与えている状態だったようです。結局役に立たないけれど試しに入れたコードが残ってしまって…は、こういうゆるい開発プロセスではAIの有無によらずやりがちな失敗です。調査と実装のブランチを分ける、そもそもPRベースで変更管理して不要な処理は入れない、ということを徹底していれば防げたと思います。つまり開発環境を管理している自分がダメだったんです。ごめんやでAI。
DAY-7. 音声のテストを追加
エージェントも変えたことだしもう一つくらいテストを追加しようかな、と、今度は「音声に関するテスト」の追加を依頼しました。
サクッとやってくれましたが、「環境の問題で音声が再生されないケースがある」と、再生されなくてもワーニング表示のみでエラーにしない仕様になっていました。これはわかりづらいので、環境依存で再生されないケースは考えない、という仕様に変更してもらいました。
詳しすぎるが故の考えすぎ仕様ですね。悪くない。悪くないよこれは。
DAY-8. テスト結果を確認するGUIを追加
さて、一旦テスト実装はできました。しかし一つ新たな問題が。ここまで、テストの実行結果はコマンドラインでの実行ログで見る、スクリーンキャプチャもフォルダから直接開く、ということをやっていたのですが、テストの件数も増えてきて正直そろそろ面倒になってきました。
GUIで確認できるものを用意して欲しい、とAIに頼むと、なんとPlaywrightにはそもそもGUI用のツールが用意されているとのこと。早速使えるようにしてもらったところ、これが めっちゃ便利
テストとその結果の一覧が実装に沿ってわかりやすく並んでいる上に

テストのプロセスがコード上のどの記述に対応するのかも表示してくれます

DAY-2でも書きましたが、自分は今回の対応で Playwright を初めて知りました。Seleniumはそこそこ経験があったのでそちらの方が良し悪しの評価しやすいかな〜という考えが頭をよぎったりもしました。しかし結果的には AIのオススメに従って大正解 でした。
出来上がったテストツール
そんなわけでできたものがこちらになります。
リポジトリのREADMEには色々書いてありますが、AIが気を利かせて追加してくれただけなので自分の方では詳しく検証していません^^; なおDAY-8でUIを使えるようにしてもらいましたが、 playwright-report/index.html を開けば結果が確認できる、と、READMEに書いてありました……。ちゃんと読んでなくてごめんなAIとしか。後からテスト全体のサマリーの確認だけしたい、という場合はUIモードよりもこちらの方がわかりやすいかもしれません。
また改めて書きますと、記事上は実装プロセスをDAY-1からDAY-8と称して分けていますが、実際にやっていたのは3日くらいでした。途中で一度飽きなければ全体で1日だったかもしれない、というくらいの工数感です。
さらに、自分がやったのはAIに指示を出すことだけで、テスト用のソースコードには手を触れていません。この記事を書くという目的がなければ読むことすらしなかったと思います。これもある意味ノーコードで実装できた、と言っても過言ではないのではないでしょうか。なーんてね。
以上、ご参考になりましたら幸いです。
