テスト名は仕様の1行 — AIに命名を任せられる環境を作るまで
はじめに
本記事は、テストコードとAIをめぐるシリーズの6本目です。1〜5本目では「AI時代に、エンジニアの設計判断はどこに残るか」という問いを軸に、テスト設計力、ドキュメントとしてのテスト、人間が先に仕様を設計する作法、QA技法、フロントエンドテスト実践記まで扱ってきました。
ここから先はシリーズ本編から少し離れた単発記事を続けていきます。本記事のテーマはテスト名のリファクタリングです。前作「テストを"仕様の実行可能なドキュメント"として読み直す」で書いた「ドキュメントとしてのテスト」を、自分の現場で実践に移そうとしたとき、何を考え何を変えたかの記録です。
本記事の想定読者は、テストコードを書き始めて間もないジュニア〜中堅のエンジニア(目安として0〜5年目あたり)です。テスト名に違和感を覚えながら触れずに来てしまった方や、最近AIにテストを書かせ始めて命名の質が気になっている方にも、何かしら届く記事になればと思います。
なお、本記事の後半ではAIエージェント(Cursor、Claude Code)を業務で運用している前提の話が出てきます。AIエージェントを日常的に使ったことがない方は、まずは触れてみてから読み直してもらうと、内容を自分事にしやすいと思います。
第1章: 自分で書き直していた時期と、いま起きていること
テストコードを書き始めた頃、自分が書いた test_calculate_returns_true のような名前を、半年後に読み返して困った経験はないでしょうか。「これ、何のテストだっけ?」と中身を読みに行く。レビューで「このテスト名、何を検証しているのか分からない」と指摘されて書き直す。私自身、こういう体験を繰り返しながら、テスト名を実装の言葉から仕様の言葉に書き換える作業を、業務のなかで少しずつ進めてきました。
最近の自分の仕事を振り返ってみると、テスト名そのものを直接書く時間が減っています。実装と一緒にAIエージェント(私の現場ではCursorとClaude Codeを併用しています)にテストの雛形を生成させ、出力された名前にほぼそのまま乗っている、という日が増えました。
もっとも、AIに任せれば良いテスト名が自動的に出てくる、とは限りません。プロジェクトに何のルールもない状態でAIに丸投げすれば、メソッド名や戻り値をそのまま転写したような、実装の言葉のテスト名が返ってくることが多いはずです。私の現場でAIに任せられているのは、ルール・規約をプロジェクトに整備し、AIエージェントの参照先(.cursor/rules や CLAUDE.md 相当の場所)に書き込んでいるからです。AI導入と並行して規約を育ててきた、という経緯になります。
本記事では、この「任せられる状態を作る」ために何を整理したか、そしてその過程で自分の中で何が変わったかを書きます。
第2章: ダメな名前と良い名前の対比
ルール・規約の話に入る前に、何を直したいのかを具体例で共有します。架空のユーザー認証機能のテストを題材に、日本語のテスト名のペアを3つ並べてみます。
ペア1: 実装の言葉から仕様の言葉へ
// before
public function test_authenticateメソッドの戻り値がtrueになる(): void
{
$result = $this->auth->authenticate('user@example.com', 'correct_password');
$this->assertTrue($result);
}
// after
public function test_正しいメールアドレスとパスワードの組で認証に成功する(): void
{
$result = $this->auth->authenticate('user@example.com', 'correct_password');
$this->assertTrue($result);
}
beforeはメソッド名と戻り値をそのまま日本語で並べているだけで、業務上の意味が読み取れません。仕様の言葉に置き換えると、「正しいメールアドレスとパスワードの組」という前提と、「認証に成功する」という期待結果が、テスト名の時点で見えるようになります。
ペア2: 期待結果が抜けているケース
// before
public function test_誤ったパスワードで認証する(): void
{
$this->expectException(AuthenticationException::class);
$this->auth->authenticate('user@example.com', 'wrong_password');
}
// after
public function test_誤ったパスワードでの認証は失敗する(): void
{
$this->expectException(AuthenticationException::class);
$this->auth->authenticate('user@example.com', 'wrong_password');
}
beforeは前提だけが書かれていて、結果として何が起きるべきかがテスト名から読み取れません。afterでは「失敗する」という期待結果まで名前に含めました。
なお、Exceptionの種類(AuthenticationException)まで名前に含めるかどうかはチーム規約による判断です。私のチームでは test_誤ったパスワードでの認証は失敗する のように振る舞いだけを書く方針で揃えており、afterはこの形で示しました。test_誤ったパスワードでの認証はAuthenticationExceptionを投げる のようにクラス名まで書く方針もありますが、種類の違いはテスト本体のアサーションで検証されればテスト名から外す、というのが私たちの選択です。
ペア3: 番号付きを仕様の言葉に書き直す
// before
public function test_トークン検証その1(): void { /* 期限切れトークン */ }
public function test_トークン検証その2(): void { /* 不正な署名 */ }
public function test_トークン検証その3(): void { /* 別ユーザーのトークン */ }
// after
public function test_有効期限切れのトークンは検証に失敗する(): void { /* ... */ }
public function test_署名が不正なトークンは検証に失敗する(): void { /* ... */ }
public function test_別ユーザーのトークンは検証に失敗する(): void { /* ... */ }
beforeは「トークン検証」というテーマだけ示して、個々のケースを番号で区別しています。これは、Excelの設計書の項番と紐づいた運用方針の現場ではよく見られるケースだと思います。
それはそれで1つの管理方法ではありますが、この場合テストファイルが伸びるほど、それぞれが何を検証しているかは中身を読まないと分からなくなっていきます。
afterの形なら、ファイルを上から眺めるだけで「このシステムはトークンに対してこういう失敗ケースを定義している」と読み取れます。
いずれにせよ3つのペアに共通しているのは、テスト名は実装の言葉ではなく、仕様を語る言葉で書きたい、という姿勢です。これはテストファイルを「仕様のドキュメント」として読めるようにするための最小単位の作業でもあります。
第3章: AIに意図した命名をさせるための、ルール・規約の整え方
第2章のような命名を、自分が直接書かなくても出てくるようにするには、AIエージェントが参照する場所にルールを置けばいいのでは?そう気付いたのが現在の運用の出発点でした。私の現場では .cursor/rules 配下や CLAUDE.md 相当の場所に、テスト命名の規約を1ファイルとしてまとめています。骨子は次の3点です。
(1) 命名のフォーマットを明示する
これは命名の形式に関するルールです。「test_<前提条件>_<期待結果> の形を基本とする」「日本語可」「メソッド名やクラス名を直接含めない」といった構文レベルの指針を書いています。曖昧さの余地を減らすために、良い例と悪い例をルールファイルに併記しています。第2章のbefore/afterに近いペアを、ルール内のサンプルとして置いている形です。
(2) 仕様の言葉で書く、という方針を明示する
こちらは命名の意味に関するルールです。形式が整っていても、メソッド名や戻り値をそのまま並べただけでは「仕様を語る」状態にならない。だから「実装の戻り値や呼び出すメソッド名ではなく、業務上の前提と期待結果を名前に含める」という方針を書いています。私が試したCursorとClaude Codeでは、ルールファイル中の指示文を一字一句守るというより、全体の方向性を読み取って近い出力を返してくる傾向がありました。規則を細かく列挙するより、方針を言語化するほうが効きやすい、というのが手応えとしてあります。
(3) ドメイン用語集との接続を明示する
業務固有の語彙の対訳をまとめたファイル(以前の記事ではドメイン用語集と呼びました)を、命名規約と同じ場所か隣に置いています。「認証」「会員」「請求」など、プロジェクトで使う言葉の対訳と、対応するクラス名・メソッド名を一覧にしておく。AIエージェントはここを参照して命名を返してくるので、業務の言葉とコードの言葉のズレが減ります。
この3点を整備したあとは、AIから返ってくるテスト名が安定するようになりました。test_authenticateメソッドの戻り値がtrueになる のような実装の言葉ではなく、test_正しいメールアドレスとパスワードの組で認証に成功する に近い出力が、初手から得られる頻度が上がった、という変化です。
ルール・規約の整備は、自分が直接命名する作業を、AIが代行できる型に落とし込む作業だとも言えます。
第4章: うまくいかなかったこと
ルール・規約を整えればすべてうまくいくかというと、そうでもありません。私が踏んだ躓きを2つ書いておきます。
一つは、ルールを抽象的に書き過ぎたときです。最初に書いたルールは「仕様を語る名前にする」程度の指示で、AIエージェントは何度生成しても揺れる名前を返してきました。具体例(良い名前と悪い名前のペア)をルールファイルに書き込んでからは出力が安定するようになりました。抽象的な指示は実例で補強する必要がある、というのは現場で繰り返し感じたことのひとつです。
もう一つは、新しい業務領域に踏み込んだときです。プロジェクトに新機能(たとえば多要素認証やSSO連携)を追加したとき、ドメイン用語集が追いついておらず、AIがその領域だけ実装の言葉でテストを書いてしまった時期がありました。
新しい業務概念が入るタイミングで、自分が用語集をメンテナンスしないと、AIの出力もそこで止まります。
ルールは育てるもので、書いて終わりにできるものではないということです。
このあたりは、ルール整備というよりも継続的なメンテナンスの話で、自分の仕事の一部として残ります。AIに任せても、自分やチームの誰かが土台を耕し続けないと、出力は再び揺れ始めます。
第5章: 名前を書く時代から、書ける環境を整える時代へ
1ヶ月以上この運用を続けてみて、自分の中で起きた変化を一言にすると、テスト名を直接書く時代から、AIが良い名前を書ける環境を整える時代へ、と表現したくなる移り方でした。
少し前までの自分は、毎日テスト名と向き合って、ひとつずつ言葉を選んでいました。レビューで指摘されることも多く、自分の言語感覚が試されているという感触もありました。今はテスト名そのものを書く時間がほとんどなくなり、代わりにAIエージェントが命名できる環境を整える時間に重心が移っています。ルールを書く、用語集を整える、新しい業務概念を翻訳する、AIの出力を眺めて規約のズレを見つける、などに時間を費やすようになっていきました。
シリーズの主軸である「設計判断は人間に残る」という主張は、こういうところで形になって効いてきます。命名そのものはAIに任せられても、何を仕様として認識し、どう言葉にするかの判断は依然として自分に残っています。
テスト名のリファクタリングが、テスト名ファイルから規約ファイルへと作業の場所を移しただけ、という言い方もできるかもしれません。
これからテストコードを書き始める方や、書き始めて間もない方には、もう一つ別の入口があります。いきなり良いテスト名を書こうとしなくていい、という入口です。
AIに任せて出てきた名前を、規約と照らし合わせて違和感を持つ。違和感を言語化して規約に書き戻す。この往復のなかで、テスト名に対する自分の感覚は育っていきます。規約に書き込む過程で、自分が漠然と感じていた「良い名前と悪い名前の境目」が言葉になっていく——そのプロセスを通じて、自分で全部の名前を書こうと頑張っていた頃より、命名そのものへの解像度が上がってきている、というのがここまでやってきての手応えです。
最後に一つ補足しておくと、本記事ではテスト命名と、それを支える規約整備に絞って書きました。実際には、AIに良いテストを書かせるには、ドメイン理解の深まりやレビュー文化、テスト戦略の合意など、複数の要因が絡み合っています。本記事はそのうちの一面、という位置付けで読んでもらえるとありがたいです。
Discussion