Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
生成AIを使ったテスト記述の最適化と生産性向上
Search
Ian Yamamoto
March 15, 2024
Technology
700
0
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
生成AIを使ったテスト記述の最適化と生産性向上
Ian Yamamoto
March 15, 2024
Other Decks in Technology
See All in Technology
GitHub Copilot運用のリアル ~AI Credit時代にどう向き合うか~
takafumisu2uk1
0
480
BPaaSで進むAIオペレーションの現在地 AI実装が効く領域とスケーラビリティの選定と実装
kentarofujii
0
210
2026-06-24_人とAIの責務分離に基づく開発プロセスの提案.pdf
takahiromatsui
0
240
iOS アプリの「これって不具合ですか?」を AI に調べてもらう
miichan
0
150
MySQL & MySQL HeatWave Report - June 2026
freshdaz
0
190
Agile and AI Redmine Japan 2026
hiranabe
4
500
スタートアップにAmazon EKSは早すぎる? マルチプロダクト戦略を加速する Platform Engineeringの実践 / Is Amazon EKS Too Soon for Startups? Practical Platform Engineering to Accelerate a Multi-Product Strategy
elmodev09
1
1.9k
製造現場での生成AIの活用、およびエージェントAIの実装のあり方、AVEVAの取り組み
iotcomjpadmin
0
180
技術・能力を向上する原理原則 #きのこセッションa #きのこ2026
bash0c7
0
140
IaC コードを資産へ:AWS CDK 社内ライブラリと横断展開 / aws-summit-japan-2026
gotok365
10
1.6k
5分でわかるDuckDB Quack
chanyou0311
4
260
#エンジニアBooks 30分でわかる 「技術記事を書く技術」 / engineer-books 2026-06-30
jnchito
1
120
Featured
See All Featured
Mind Mapping
helmedeiros
PRO
1
260
StorybookのUI Testing Handbookを読んだ
zakiyama
31
6.8k
A designer walks into a library…
pauljervisheath
211
24k
Data-driven link building: lessons from a $708K investment (BrightonSEO talk)
szymonslowik
1
1.1k
Embracing the Ebb and Flow
colly
88
5.1k
Claude Code のすすめ
schroneko
67
230k
Scaling GitHub
holman
464
140k
Self-Hosted WebAssembly Runtime for Runtime-Neutral Checkpoint/Restore in Edge–Cloud Continuum
chikuwait
0
620
[SF Ruby Conf 2025] Rails X
palkan
2
1.1k
AI: The stuff that nobody shows you
jnunemaker
PRO
8
740
HDC tutorial
michielstock
2
720
Practical Orchestrator
shlominoach
191
11k
Transcript
生成AIを使ったテスト記述の最 適化と生産性向上 山本 イアン JaSST'24 Tokyo
自己紹介 山本 イアン (Ian Yamamoto) • 新卒でIT業界へ ◦ toBの社内向けシステム開発 ◦
自治体向けパッケージソフトの保守運用など • SaaS系ソフトの自社サービス開発会社に転職 ◦ 開発以外に自動テストの導入を行う ◦ APIテストや静的解析、コーディング規約の導入なども • LegalOn Technologiesに入社 ◦ Software Engineer in Testとして活動を始め、今に至る
今回話すこと • 受託システムの開発をしていたときの状況 • スケジュールがタイトであり、手動テストがやっとの中、なんとかAPIテストメインの 自動化にたどり着くことができた • 自動化のリソース確保の鍵となったのが生成AI 何を問題と感じて生成AIを使用したテストにたどり着いたのか? 生成AIでどんなテストコードを書く工夫をしたのか?
を話します。
問題提起と目標の掲示
3人のエンジニアで受託開発を請け負い、フルスクラッチで開発。 そしてQAもいないという状況で手動テストを行う。 リグレッション対応の工数もそれなりにかかってしまう。 自動テストが行われれば改善できるのだが・・・ 課題:そもそもテストに割けるリソースが十分ではない。 当時のプロジェクト状況
打開策に生成AIの導入を試みる 当時(2023年1月頃)生成AIが注目を浴び始めていた。 実際にTabnineというAIを使用したコード補完ツールを導入。 →すでにダッシュボードから10%生産性があがったことがわかった。 ただし、導入後にそれ以上の生産性を上げる議論は出なかった。 もちろん、テストコードの書き⽅に関する議論もなかった。 課題:AIは導入で終わりがちで、テストの書き方の議論もない。
課題のおさらい: 1. そもそもテストに割けるリソースが十分ではない 2. 生成AIを導入しても一定の成果で止まりがち 3. どう書けばAIがテストコードを生成しやすいかという議論が発生しにくい これらの解決のために、 • どのようにテストを書いていくか
• どのような補完が望ましいか • どう補完をチューニングしていくか という観点に絞り、私の経験に基づいて解決策の考察を発表します。 課題の再整理と目標の提示
ツール・技術環境について
テストケースやビジネスロジックのサンプル 基本は結合テストメイン。 • APIテスト ◦ 正常系/異常系/複数系 • バッチ処理のテスト • AWS
S3などの外部サービス連携のテスト 機能は基本的なWebアプリの機能 • ユーザー検索(管理者・企業管理者) • プロフィール機能 • メール通知機能
技術スタック 言語・フレームワーク:PHP 8.1 (Laravel 9.x), Intertia(Single Page Application) 0.6 テスト実行ツール:PHPUnit
9.5 Linter / Formatter: PHP_CodeSniffer / PHP CS Fixer データベース:MySQL 8.x AIツール:GitHub Copilot / ChatGPT 3.5 - 4 ※プロジェクトの初期にTabnineを使用していましたが、トライアルなどの都合もありGitHub Copilotに移行しています。
基本戦略やテストの書き方
まずは「大きな補完」を目指す GitHub Copilotのような補完ツールは細かい補完より大きな補完の方が得意。 その上、大きな補完のほうが生産性向上に寄与しやすい。 そのために • コード補完AIがどんなパターンで次のテストが書かれるかを知っている • それぞれのテストで、各実装者の書き方のクセが少ない •
AIにとって死角となるような情報が適宜補足されている というテストの書き方をする。
サンプルコードの前提 企業と一般ユーザーで何らかの交流ができるWebアプリ 企業ユーザー、一般ユーザー以外にもアプリの管理者がいる • 企業ユーザー … CompanyMemberテーブル • 一般ユーザー …
Userテーブル この前提で、フィクスチャを用意後、管理者が企業ユーザーのテストコードを 手動で書き、その後に一般ユーザーの検索機能のテストを書く。 ⼀般ユーザーの検索機能のテストから「⼤きな補完」が出始めるはず
テスト対象コード(企業ユーザー検索) public function index(Request $request) { $keyword = $request->search_text ??
''; $companyMembers = CompanyMember::where('name', 'like', '%' . $keyword . '%') ->orderBy('updated_at', 'DESC') ->paginate(10) ->withQueryString(); // 更新すべきデータとともにページをバックエンドから返す return Inertia::render('Admin/CompanyMember/Index', [ 'companyMembers' => $companyMembers, ]); } リクエストからもらった名前でLIKE検索をするシンプルな実装。
テストクラスやフィクスチャを作成 class AdminCorporateSearchTest extends TestCase { // 毎テストごとにデータベースをリセット use RefreshDatabase;
// ログインするユーザー private $admin; // 管理者作成フィクスチャ protected function setUp() : void { // PHPUnitのsetUpメソッドを呼び出し、テスト環境を初期化 parent::setUp(); // 管理者ユーザーをDBに作成 $this->admin = AdminUser::factory()->create([ 'user_div' => Const::USER_ADMIN, ]); // 作成した管理者ユーザーでログイン $this->actingAs($this->admin); } テスト毎にDBを初期化し、管理者としてログインするシンプルなフィクスチャ 最初は小さな補完から。 解説のため毎行コメント記述、 改行少なめとなっています。 実際のコメントは 「管理者作成フィクスチャ」 のみですが、重要です(後述)
テストコード(企業ユーザー検索) 1. テストデータの作成 a. 田中 太郎 b. 田中 花子 c.
山田 次郎 2. 「田中」で検索 a. 田中太郎、田中花子がヒットし ます 3. レスポンスをアサート a. 指定したページを返したか b. ヒット数が2名か c. それぞれのIDをベースにヒット した名前が正しいか public function test_search_company_members_by_name () { // テストデータの準備 $member1 = CompanyMember::factory()->create(['name' => '田中 太郎 ']); $member2 = CompanyMember::factory()->create(['name' => '田中 花子 ']); $member3 = CompanyMember::factory()->create(['name' => '山田 次郎 ']); // 検索キーワード「田中」でフィルタリングされた結果をテスト $response = $this->get(route('admin.corporate.index', ['search_text' => '田中'])); $response->assertInertia(fn ($page) => $page ->component('Admin/CompanyMember/Index') ->where('companyMembers.data', fn ($members) => count($members) === 2 && collect($members)->pluck('id')->contains($member1->id) && collect($members)->pluck('id')->contains($member2->id) && !collect($members)->pluck('id')->contains($member3->id)) ); } 注:DocStringは省略しています
テスト対象コード(一般ユーザー検索) public function index(Request $request) { $keyword = $request->search_name ??
''; $members = User::where('name', 'like', '%' . $keyword . '%') ->orderBy('updated_at', 'DESC') ->paginate(10) ->withQueryString(); return Inertia::render('Admin/User/Index', [ 'users' => $members, ]); } 参照先のみが変わる実装。次にAIをつかっていきます。 ※手動で実装
まずDocStringを入力し、テスト名を入力する DocStringとテストは恐らく別々に生成されます(2回の補完) テスト名はきちんとユーザーを検索する旨を記述する /** * 管理者が一般ユーザーを検索できることをテスト * * @return void
*/ public function test_search_users_by_name() この後は暫く長い読み込みが続きます。そして「大きな補完」が来ます
生成されたテストコード(一般ユーザー検索) public function test_search_users_by_name() { $user1 = User::factory()->create(['name' => '田中
太郎']); $user2 = User::factory()->create(['name' => '田中 花子']); $user3 = User::factory()->create(['name' => '山田 次郎']); $response = $this->get(route('admin.user.index', ['search_text' => '田中'])); $response->assertInertia(fn ($page) => $page ->component('Admin/User/Index') ->where('users.data', fn ($users) => count($users) === 2 && $users[0]['name'] === '田中 太郎' && $users[1]['name'] === '田中 花子' ) ); } 前のテストコードと違う点 • テスト名 • テストデータ • ルート名 • アサート方法 それ以外は同じ。 テストデータは書き方が同じであ ることが重要 生成したテストが正しいことより、テストの流れを再現できたことが重要
より効率的にテストを生成する
「次」を予測しやすいテストの書き方 テストケースの順番を統一するのが効果的。 例として、 1. フィールドやフィクスチャ、クリーンアップなどを定義 2. 正常系テスト 3. 異常系テスト のような順番で常に書かれるようにする。
そうすることによってAIは次のテストケースの推測を外しにくくなる ※例外的に、多少の割り込みがあっても大丈夫です
複雑なテストは先に書いたほうが補完が効きやすい 下記のようなテストの書き方は、AIにとって複雑なテストとなりがち。 • 単数形 → 複数形 • 任意のオプションがないケース → 任意を含めたケース
• ハッピーパス → 網羅的なパス • ページネーションなし → ページネーションあり これらは、逆の順番で書いたほうがAIにとっては提案しやすい 常にAIへ”全体像”を見せてくれるケースを優先して書く
ケースの分岐はコメントを使って紐付ける 複数行のブロックの実装の一部だけを分岐させたい場合もある。 フィクスチャの分岐例: • 管理者を作成し、ログインする • 通知のモックを作成し、一般ユーザーとしてログイン フィクスチャは用途によって異なることが多いため、 戦略がないと一行ずつ生成されるか、間違ったフィクスチャを一気に生成しがち。 これをコメントを使って解消する。
フィクスチャを「大きな補完」へ昇華させる例 それぞれのフィクスチャにコメントを つける。 例のように、フィクスチャをコメント で置いても、別ファイルで実装部分の みをコメントアウトした状態ですべて の⾏を出⼒できる。 更に、テストクラス名にNotification があれば通知フィクスチャを提案す る。
タグをつけるイメージで記述する。 class AdminUserProfileTest extends TestCase { … // 管理者作成フィクスチャ protected function setUp() : void { parent::setUp(); $this->admin = AdminUser::factory()->create([ 'user_div' => Const::USER_ADMIN, ]); $this->actingAs($this->admin); } // // 通知を受け取るユーザー作成フィクスチャ // protected function setUp() : void // { // parent::setUp(); // Notification::fake(); // $this->admin = AdminUser::factory()->create([ // 'user_div' => AppConst::USER_DIV_ADMIN, // ]); // $this->actingAs($this->admin); // } プロンプト(⻑めの⽂章)を書いて⽣成するというよりは、 正確にタグ(短い⽂章や単語)の実装を繰り返して認識させるイメージがいい
「小さな補完」はAIの死角をクリアする情報を出す ⼤きな補完の修正やフィールドは「⼩さな補完」戦略で⾏く 重要なのは死⾓を意識すること。すなわち⽣成AIの視点を持つ。 • DBスキーマもあまり⾒えない ◦ 状況によっては、制約などもシンプルなプロンプトにいれたほうがいい ◦ そのテストを書く量にも注意。 •
APIのペイロードもあるとスムーズ ◦ per_pageなど何ページで送られるのかなどの単位もプロンプト化する価値はある
複数行の説明で小さな補完を助ける 会社名を表⽰する画⾯のテスト ページネーションなどの情報がある はずなのに、補完されない いつもの型は⼤きく補完できるが、 本来あるべき詳細なAPIペイロード を推測できない(AIの死⾓) public function test_companies_are_displayed_correctly()
{ $response = $this->get(route('admin.company.index')); $response->assertInertia(fn (AssertableInertia $page) => $page ->component('Admin/Company/Index') ->where('companies', []) ); } プロンプトがない場合の補完
APIペイロードを補完する例 /* API Payload companies.data companies.current_page 1 companies.per_page 6 companies.last_page
companies.total */ … public function test_companies_are_displayed_correctly () { $response = $this->get(route('admin.company.index' )); $response->assertInertia(fn (AssertableInertia $page) => $page ->component('Admin/Company/Index' ) ->has('companies.data', 3) // API Payload ->where('companies.data', 3) ->where('companies.current_page' , 1) ->where('companies.per_page' , 6) ->where('companies.last_page' , 1) ->where('companies.total', 3) ); } APIペイロードのプロンプトを仕込 み、テストを⽣成 実際は1⾏ずつの補完となるため、 ⼤きい補完への繋ぎとして利⽤す るのがよい 作成後コメント部分は削除推奨 DBも基本は同じ
過剰な最適化を避ける • ルーティング情報は埋もれがち ◦ クロージャなどは便利だが、⽣成AIにとってコンテキストが離れがち ▪ その際はプロンプトで表などを仕込むか⼿動でコーディングするほう が効率的 ▪ そもそもテスト関数名以外はあまりみていない可能性が⾼い
▪ 検索画⾯=searchのように、どうしても関連する単語を使いたがる • クレデンシャル情報は⽣成が途中で途切れる ◦ 順番を変えられるなら最後の⽅に持っていく ◦ 専⽤の定数を持つと途切れることを回避できる ▪ TEST_EMAIL, TEST_PASSWORDなどを使う コピペや⼿⼊⼒など、他の代替案が早いかを常に意識する。
AIツールの導入基準について
どのように補完ツールを選ぶといいのか 補完ツールを選ぶ際は下記の基準が参考になると思います。 1. 「⼤きな補完」がしやすいか 2. ⼀定のコメントに対応したスニペットを安定して補完するか 3. ダッシュボードが提供されているか ※ただしダッシュボードなどの機能は有料化することがあるのでご注意 トライアル期間のうちに⼗分なテストを検証できる準備も⼤事です
対話型AIについて • メリット ◦ 細かい条件を調整しやすい ▪ その過程をプロンプト化できる ◦ 共有ができる ◦
質問やエラー解決も可能 • デメリット ◦ 応答までに時間がかかる ◦ 機密情報を書いてしまう可能性がある ◦ チャットの⻑さや頻度に上限を設けているケースがある 対話型AIでも⼤きな補完を意識する
導入後の反省点やまとめ
大きな補完が共通基盤となりテストが書きやすくなる 今回の検索機能で作られた下記の構成が基盤となり、他のテストでも補完が効く • フィクスチャ • テストデータ • アサート⽅法 • 次のテストパターン
個人的に後悔した点・もっとやれたこと プロンプトを資産とすることができなかった。 私としては、どういったプロンプトでどういうコードが⽣成されたかを何らかの ドキュメントなどに含める価値はあったと思う。 それができず、単にすこし⽣産性を上げただけで、チーム全体のAIリテラシーに 結びつけるに⾄らなかった。 なのでプロンプトをドキュメント化して共有し、バージョニングなどもしておく といいと思います。
まとめ • 「⼤きな補完」を最適化することによって⽣産性を⾼める • AIの視点に⽴ってコードの改善ポイントを探り、死⾓をなくす • プロンプトや改善⼿法も資産と考え、アップデートを続ける姿勢を持つ
なんとなくタブキーを押すだけじゃ、もったいない! 完
質疑応答
1.他の作業者が同じようにテスト⽣成したいと思った場合、資産化したプロンプトの構 ⽂を真似して⼊⼒してもらうイメージでしょうか? その通りです。厳密にはGitHub Copilotでは、プロンプトだけではなくこれまでの実 装と、どういう提案を受け⼊れ/拒否したかが重要な資産となります。 なのでGitHub Copilotでいえば、他の作業者がテストを作成し、Copilotに既存の実装 などの資産を参考に⼊⼒してもらい、修正があれば⾏っていくイメージです。 2.プロダクトコードやコメントを元にしたテストコードになると、テストとして成⽴し ないのではないのでしょうか?
プロダクトコードの正当性は補完戦略とは別に検証する必要があります。 この発表はテストコード記述の⽣産性向上にフォーカスしておりますが、コード補完 のみでテストが完結することを推奨しているわけではありません。 どのようなコードを補完すればいいのか?という問題はそれぞれの開発者に委ねられ ているのがコード補完の現状であり、⽣成AIは良くも悪くもコードを沢⼭書けるツー ルである、という意識が重要です。
3.あるクラスに対して必要なテストすべてを、⼀気に補完してもらうことって、難しいで しょうか?似た機能を持ったクラスのテストが、すでにプロジェクト内にあれば、補 完できそうでしょうか? 完全に補完を受け続けた場合、1テストケースの単位を超えて⻑い補完がどんどん⻑ くなっていきます。 つまり1回の補完でも、理論上は可能だと思いますし、「⻑い補完」を複数回に分け るアプローチでもかなり素早くできると思います。 これを実現するにはおっしゃるとおり、似たような実装が予め必要だと思います。 4.⽣成AI側がアップデートされると、必要とされるプロンプトも変わってしまう可能性 はあるのでしょうか?
GitHub Copilotのアップデートに関しては、短い補完は結果が変わることよくありま したが、⻑い補完に関しては問題なく使えています。 本登壇で「⻑い補完」を強調したのは、これらのアップデートに耐えてきたから、と いうのもあります。