🔄第1章 TDDの基本サイクル:レッド・グリーン・リファクタリング
テスト駆動開発の核となるのは、「レッド・グリーン・リファクタリング」という短いサイクルです。 TypeScriptを使うことで、型安全性もこのサイクルの中で意識できます。
🤖AI開発時代におけるTDDの再評価
⚠️「TDD is Dead」論争とその背景
2003年にKent Beckによって体系化されたテスト駆動開発(TDD)は、長年にわたって多くの開発者に支持されてきました。 しかし、2014年にDavid Heinemeier Hansson(Ruby on Railsの作者)が「TDD is Dead」と発言し、大きな論争を巻き起こしました。
- テストファーストの厳格さが開発速度を阻害する
- 過度なモックやスタブがテストを脆弱にする
- 設計の柔軟性を損なう可能性がある
- すべてのコードにテストが必要とは限らない
🚀AI開発時代におけるTDDの新たな価値
AI技術の発展により、TDDは従来とは異なる新しい価値を持つようになりました。 AIエージェントが自律的に開発を進める現代では、テストの役割がより重要になっています。
1. AIがテストを自動生成
AIは要件を読み取って、人間よりも網羅的なテストケースを短時間で作成できます。 エッジケースや境界値テストも自動で含めるため、テスト品質が向上します。
2. 人間とAIの共通言語
テストコードは「何を作りたいか」を明確に表現する実行可能な仕様書です。 AIはテストから正確に要件を理解し、人間もAIの作ったコードの意図を把握できます。
3. 高速開発の安全装置
AIは人間の何十倍も速くコードを書けますが、エラーも高速で蓄積されます。 テストがあることで、大量の変更やリファクタリングを安全に実行できます。
- 1. レッド:動作しない、おそらく最初のうちはコンパイルも通らないテストを1つ書く。
- 2. グリーン:そのテストを迅速に動作させる。このステップでは罪を犯してもよい。
- 3. リファクタリング:テストを通すために発生した重複をすべて除去する。
この章では、非常に簡単な例題を通して、このサイクルを実際に体験してみましょう。 AI開発時代においても、この基本的なサイクルは変わりませんが、 AIツールとの協働により、より効率的で安全な開発が可能になります。
📝例題:単純な足し算関数 (TypeScript)
2つの数値を引数に取り、その合計を返す add
という名前の関数をTDDで開発します。
TODOリスト
- •
add(1, 2) は 3 を返す
🔴1. レッド:失敗するテストを書く
まず、add
関数のテストファイルを作成します。lib
フォルダーを作成し、 その中に add.test.ts
というファイルを作成しましょう。
import { add } from './add'; // add.ts はまだ存在しない
describe('add関数のテスト', () => {
it('1と2を足すと3を返すこと', () => {
expect(add(1, 2)).toBe(3);
});
});
この時点では lib/add.ts
ファイルもadd
関数も存在しないため、このテストは失敗します。 ターミナルでテストを実行してみましょう。
npm test
Jestがエラーを出力し、テストが赤く(失敗)表示されるはずです。これが「レッド」のフェーズです。 エラーメッセージの例: Cannot find module './add' from 'lib/add.test.ts'
など。
🔍テスト失敗メッセージを読み解こう
テストが失敗すると、以下のようなメッセージが表示されます。 最初は怖く見えるかもしれませんが、実はとても親切なメッセージなんです:
これは正常な流れです! TDDでは「まず失敗するテストを書く」ことから始めるので、 この段階でテストが失敗するのは期待通りの動作です。
🟢2. グリーン:テストをパスさせる最小限のコードを書く
次に、テストをパスさせるための最小限のコードを lib/add.ts
に記述します。 TypeScriptなので、関数の引数と戻り値にも型を付けます。
export function add(a: number, b: number): number {
// テストをパスさせるための最も簡単な実装
return 3; // 今回は期待値が3なので、直接3を返す
}
書籍で言うところの「仮実装」です。
失敗するテストを書いてから、最初に行う実装はどのようなものだろうか――ベタ書きの値を返そう。
再度テストを実行します。
npm test
今度はテストが成功し、緑(成功)で表示されるはずです。これが「グリーン」のフェーズです。 この段階では、コードの品質や汎用性は問いません。とにかくテストをパスさせることが目的です。
🎉テスト成功!何が検証されたの?
テストが成功すると、以下のようなメッセージが表示されます。 これは単なる「動いた」以上の意味があります:
🔍実際に検証されたこと
- •
add
関数が正しく定義されている - • 引数
1, 2
を渡すと期待値3
が返される - • TypeScriptの型チェックも通過した(
number
型の引数と戻り値) - • モジュールのimport/exportが正しく動作している
🛡️これで安心できること
- • 将来コードを変更しても、この基本機能が壊れていないことを即座に確認できる
- • 他の開発者がコードを見ても、「この関数はこう動くべき」という仕様が明確
- • リファクタリング時に、外部から見た動作が変わっていないことを保証できる
🔵3. リファクタリング:コードを改善する
テストが通る状態になったので、安心してコードを改善(リファクタリング)できます。 現在の add
関数の実装は、 特定の入力(1と2)に対してのみ正しい結果を返しますが、汎用的ではありません。 これを汎用的な足し算のロジックに修正します。
export function add(a: number, b: number): number {
return a + b; // より汎用的な実装にリファクタリング
}
リファクタリング後も、再度テストを実行して、変更によってテストが壊れていないことを確認します。
npm test
テストは引き続き成功するはずです。これが「リファクタリング」のフェーズです。 この「レッド → グリーン → リファクタリング」の短いサイクルを繰り返しながら開発を進めていくのが、TDDの基本です。
📋TODOリストの更新と新しいテストの追加
最初のTODO項目は完了しました。 次に、別のケースをテストしてみましょう。たとえば、負の数の足し算です。
新たなTODO項目
- • ✅
add(1, 2) は 3 を返す
- •
add(-1, -2) は -3 を返す
lib/add.test.ts
に新しいテストケースを追加します。
import { add } from './add';
describe('add関数のテスト', () => {
it('1と2を足すと3を返すこと', () => {
expect(add(1, 2)).toBe(3);
});
// 新しいテストケース
it('-1と-2を足すと-3を返すこと', () => {
expect(add(-1, -2)).toBe(-3);
});
});
テストを実行します。
npm test
現在の add
関数の実装 (return a + b;
) はこの新しいテストケースもパスするはずです。 もし、この新しいテストで既存の実装が失敗した場合(レッド)、再度グリーンフェーズに戻り、実装を修正し、その後リファクタリングを行います。 今回はリファクタリングの必要はなさそうです。
🚀コミットとCIの確認
一区切りついたら、変更をコミットしましょう。
git add .
git commit -m "feat: add関数をTDDサイクルで実装 (TypeScript)"
git push
GitHubにプッシュすると、先ほど設定したGitHub ActionsのCIワークフローが自動的に実行されます。 GitHubのリポジトリーページの "Actions" タブで、ワークフローが成功した(テストがパスした)ことを確認してください。
🎯まとめ
これで、TDDの基本的なサイクルをTypeScript環境で体験できました。 AI開発時代において、TDDは単なる開発手法を超えて、AIエージェントとの協働を支える重要なインフラストラクチャーとなります。
次章以降で学ぶこと
- AIツールと連携したTDD開発フロー
- 複雑なReactコンポーネントのテスト戦略
- AIエージェントが理解しやすいテスト設計
- 継続的インテグレーションでの自動品質保証
次の章からは、このサイクルを使いながらTODOアプリの機能をApp RouterとTypeScriptで少しずつ開発し、 実際のプロダクト開発におけるAI協働TDDを体験していきます。