
サービス開発部のdanaです。
弊社では全社員がClaudeCodeを利用して業務効率化を目指しています。
そのためエンジニアもClaudeCodeを用いる事が多いですが、引き続きCursorやCodexも使われています。
Slackから指示できる事が重宝し、Devinもかなり活用できています。
最近だとWindsurfがDevinに買収されたこともあり、DevinDesktopを試用したりもしています。
次の効率化はQAやE2Eテスト
AIエージェントの登場、モデルの性能向上によってコーディング、設計のスピードはかつて無いほど上がりました。
一昔前まではAIの出力したテストコードは信用できないみたいな話もちらほらありましたが、最近は実用的なテストコードも出力されるようになった印象です。
そこで、PRの内容を読み解き自動でE2Eテストをさせることはできるのではないか、という発想から実際に構築を行いました。
エージェントの選定では、下記の理由からDevinを採用しています。
- 全員が使える(使っている)
- 他エージェントと比較しても精度の高いテストが作成できた
特にDevinの強みを感じたのはURL特定です。
例えば単純な文言調整のPRの場合はDiffから取れる情報が少なく、Grepなどを用いて実際に呼び出されるコントローラーやURLを特定する作業が発生します。
この作業はそれなりのコストが発生する上に精度があまり高くありませんでした。
(これはFuelPHPで保守し続けている影響の可能性はありますが)
Devinの場合はDeepWikiにより事前にアーキテクチャやコード上での繋がりを把握しているため、テストを行うためのURLの生成までが現実的な時間で行う事ができました。
v1: Devinに全部やらせる
まず最初に思いついた手法として、Devin自身にテスト項目を作成してブラウザ操作をさせる手法です。
URLも特定できているのであれば、全てのQAを自動化できるはずです。
ただし、この手法には落とし穴がありました。スペック不足です。
ローカル環境の起動に時間がかかる都合上、ACUの浪費と性能低下が顕著に発生しました。
※今現在ACUという概念は無くなっているので、再度試したら少し違う結果になるかもしれません
そのため、立ち上げ済みの外部テスト環境を使う案も検討しました。幸いDevinからアクセスされるIPは公開されています。
それらを許可リストに設定する事で外部テスト環境にリクエストを飛ばすことはできますが、外部にテスト環境を置くと他PRで走らせたときにコンフリクトする問題が発生します。
解決するためにはブランチ切り替えのロックだったり、自動でテスト環境を立ち上げるなどがありますが、対応すべき事が多すぎました。
試してみたいだけなのにインフラ構成にまで手を出したくはありません。
テストケースの作成能力は活用できると感じたので、v2の手法に切り替えることにしました。
v2: Devinでテストを作成し、Playwrightを実施する
技術スタック
| 役割 | 技術 |
|---|---|
| テスト実行 | Playwright (TypeScript) |
| テスト対象URL特定・シナリオ生成 | Devin |
| 生成コードのレビュー・修正 | Claude |
実行フロー
GitHub上のCIにてジョブのApproveをすることで稼働する仕組み。
大まかな流れとしては下記の4ステップです。
- Devinが差分を解析してテスト対象URLを特定
- PlaywrightのTypeScriptテストコードを自動生成
- Claudeがコードをレビュー・修正
- テストを実行してPRにコメントで結果を投稿
1: Devinが差分を解析してテスト対象URLを特定
テストすべきURLを url_confidence(high/medium/low)付きで返します。
コントローラー名が特定できたとしてもrouterなどによって特定のURLが発行されたりすることもあり、それだけでURLが確定するわけではありません。
そのため実際にアクセス可能なURLをDevinに生成させます。DeepWiki活用によって明らかに精度は高い印象です。
url_confidenceが低くなるパターンとして、パラメータが複雑な場合があります。その際には、手動確認推奨、という形でエンジニアに確認を取ってもらうような仕組みにしています。
サービスに影響の無いPRで画面上で確認はできないパターンの場合はTOPページなどの複数ページでのエラーチェックに切り替えます。また、ドキュメントの修正など、完全に影響範囲外の時はこの段階で終了するようにしています。
ローカル環境向けへのURL置換
ドメインの置換もそうですが、パラメータに含まれるIDについてもDBに存在しているIDに調整します。
例えば特定の要素を持つデータでテストしたいとき、Devinは正確なローカル環境のIDを知りません。
そのためローカル環境のDBにアクセスし適切なIDを取得する、というフローがここに入ります。
万が一存在しない場合は、データ自体を登録できるような仕組みも用意しています。
2: PlaywrightのTypeScriptテストコードを自動生成
URL置換などを行った後、実際にテストシナリオを作成する対象を絞り込み、URL特定フェーズで使ったDevinセッションにシナリオ生成プロンプトを送信します。
セッションを新規作成しないことで、コンテキストを引き継ぎながら生成できる上にコストも削減できます。
3: Claudeがコードをレビュー・修正
Devinが生成したコードをそのまま実行せず、必ずClaudeによるレビューを挟みます。
生成コードはClaudeレビューに渡す前に validateSpec で安全性を検証します。
- import許可リスト:
@playwright/testのみ - 禁止パターン:
eval()/new Function()/ 動的import()/ 外部へのfetch()/process.env(テストに必要とする参照を除く)
Devin生成コード | ▼ validateSpec(安全性チェック) | ▼ Claude(セキュリティ・品質・URL突合を検証) | ▼ validateSpec(安全性チェック、Claude修正が発生した場合) | +-- カバレッジ < 30% → regenerate(Devinに再生成依頼・最大1回) | +-- URL突合チェック | page.goto() のURLとURL一覧を機械的に比較 | 不一致 → 自動修正を試みる | 解消 → そのまま判定へ | 未解消 → critical → regenerate | ▼ verdict(判定) approve → 元コードで実行 revise → Claude修正済みコードで実行 regenerate → Devinに再生成依頼(最大1回)
Claudeが自動修正した場合でも、必ずvalidateSpecは通すようにしています。どこに脅威が紛れ込むか分かりませんからね。
4: テストを実行してPRにコメントで結果を投稿
クエリログを取得するようにする
テスト実行前に MySQL general_log を有効化し、実行後にINSERT/UPDATEクエリを収集します。
最終レポートにどのようなクエリが走ったかどうかをチェックできるようにすることで、更新時に走るクエリを確認する手間なども省けます。
テスト失敗時の挙動
失敗したテストが存在する場合は、Devinに失敗テスト情報(エラーメッセージ・スクリーンショット等)を渡し、原因分析と修正案を起案させるようにしています。
CSSセレクタの誤りであればコードを確認してコード的にはOKですよとフォローしてくれたり、純粋にバグを見つけた場合はその改善案を出してくれます。
PRコメントで結果を報告する
最終的にGitHub PRへMarkdown形式でコメントを投稿します。
テスト実行結果 | +-- ✅ 成功テスト +-- ❌ 失敗テスト ............. エラー・スクリーンショット・動画パス +-- ⏭️ スキップ ............... url_confidence: low のURL +-- ⚠️ データ不足警告 ......... 存在確認でデータ0件だったテスト +-- 👀 手動チェック推奨 ....... 解決できなかったURL一覧 +-- 🔍 障害調査結果(Devin) .. 失敗時のみ +-- 📋 DB変更ログ ............. INSERT/UPDATE をテーブル別に集計
また、テストデータやAIのやり取り履歴、失敗時の画像と動画はArtifactとして後から見返せるようにしています。
まとめ
結局のところ実用レベルになってるの?
この機能は5月末にリリースし、今は様々なPRで試しつつFBを集めている段階です。
はっきり言うと 使えないことは無いが、まだまだ改善すべき内容がある といった状態です。
主に実行時間がネックになっています。20分程度かかる場合があり、URL特定できているなら手動でやれば良いという判断になってしまいます。
短い時間で質がそれなりであれば、とりあえず実行する価値があると思いますが、現状だとサイクルを回すには時間がかかりすぎです。
精度もまだまだ向上できると思います。例えば複雑な操作はまだ上手く生成できていません。
フォームの内容を生成して複数パターン自動でテストするみたいな物ができるようになったら最高ですね。
既知の問題として、大きいDiffの場合は情報が欠けてしまったりと課題は山積みの状態ですが、今後どれだけ発展させていけるのか楽しみです。
最後に
現在GameWithではAIエンジニアを募集中です。
ご興味ありましたら是非カジュアル面談をお申し込みください!