← トップページに戻る

TDD実践ガイド

🔄第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. レッド:動作しない、おそらく最初のうちはコンパイルも通らないテストを1つ書く。
  2. 2. グリーン:そのテストを迅速に動作させる。このステップでは罪を犯してもよい。
  3. 3. リファクタリング:テストを通すために発生した重複をすべて除去する。
— Kent Beck『テスト駆動開発』より

この章では、非常に簡単な例題を通して、このサイクルを実際に体験してみましょう。 AI開発時代においても、この基本的なサイクルは変わりませんが、 AIツールとの協働により、より効率的で安全な開発が可能になります。

📝例題:単純な足し算関数 (TypeScript)

2つの数値を引数に取り、その合計を返す add という名前の関数をTDDで開発します。

TODOリスト

  • add(1, 2) は 3 を返す

🔴1. レッド:失敗するテストを書く

まず、add 関数のテストファイルを作成します。lib フォルダーを作成し、 その中に add.test.ts というファイルを作成しましょう。

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' など。

🔍テスト失敗メッセージを読み解こう

テストが失敗すると、以下のようなメッセージが表示されます。 最初は怖く見えるかもしれませんが、実はとても親切なメッセージなんです:

FAIL lib/add.test.ts
● add関数のテスト › 1と2を足すと3を返すこと
Cannot find module './add' from 'lib/add.test.ts'
FAIL: どのテストファイルが失敗したか
📝
テスト名: 具体的にどのテストが失敗したか
💡
エラー内容: 「addモジュールが見つからない」= まだファイルを作っていない

これは正常な流れです! TDDでは「まず失敗するテストを書く」ことから始めるので、 この段階でテストが失敗するのは期待通りの動作です。

🟢2. グリーン:テストをパスさせる最小限のコードを書く

次に、テストをパスさせるための最小限のコードを lib/add.ts に記述します。 TypeScriptなので、関数の引数と戻り値にも型を付けます。

lib/add.ts
export function add(a: number, b: number): number {
  // テストをパスさせるための最も簡単な実装
  return 3; // 今回は期待値が3なので、直接3を返す
}

書籍で言うところの「仮実装」です。

失敗するテストを書いてから、最初に行う実装はどのようなものだろうか――ベタ書きの値を返そう。
— Kent Beck『テスト駆動開発』第28章「仮実装を経て本実装へ」より

再度テストを実行します。

npm test

今度はテストが成功し、緑(成功)で表示されるはずです。これが「グリーン」のフェーズです。 この段階では、コードの品質や汎用性は問いません。とにかくテストをパスさせることが目的です。

🎉テスト成功!何が検証されたの?

テストが成功すると、以下のようなメッセージが表示されます。 これは単なる「動いた」以上の意味があります:

PASS lib/add.test.ts
✓ add関数のテスト › 1と2を足すと3を返すこと (2ms)
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
🔍実際に検証されたこと
  • add関数が正しく定義されている
  • • 引数1, 2を渡すと期待値3が返される
  • • TypeScriptの型チェックも通過した(number型の引数と戻り値)
  • • モジュールのimport/exportが正しく動作している
🛡️これで安心できること
  • • 将来コードを変更しても、この基本機能が壊れていないことを即座に確認できる
  • • 他の開発者がコードを見ても、「この関数はこう動くべき」という仕様が明確
  • • リファクタリング時に、外部から見た動作が変わっていないことを保証できる

🔵3. リファクタリング:コードを改善する

テストが通る状態になったので、安心してコードを改善(リファクタリング)できます。 現在の add 関数の実装は、 特定の入力(1と2)に対してのみ正しい結果を返しますが、汎用的ではありません。 これを汎用的な足し算のロジックに修正します。

lib/add.ts
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 に新しいテストケースを追加します。

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を体験していきます。