テスト駆動開発(TDD)の正しい進め方はどれ?
解説
テスト駆動開発(TDD)は「まず失敗するテストを書く→テストを通す最小限のコードを書く→リファクタリングする」の3ステップを繰り返す開発手法です。このサイクルは色になぞらえてRed → Green → Refactorと呼ばれます。Redはテストが失敗している状態、Greenはテストが通った状態、Refactorはコードを整理する段階です。「実装→テスト→リファクタリング」は一般的な開発フローとしてはあり得ますが、TDDではありません。TDDの核心は実装より先にテストを書く点にあります。「設計書→実装→テスト」はウォーターフォール的な流れであり、「実装→リファクタリング→テスト」はテストが最後に来ているため、リファクタリングで壊したバグに気づけないリスクがあります。なぜ「先にテスト」なのかテストを先に書くことで、これから実装する機能の仕様を具体的なコードとして定義することになります。例えば「ユーザーの年齢が18歳未満なら登録できない」という要件があるとき、TDDではまず expect(register(17)).toBe(false) のようなテストを書きます。このテストが実装の「ゴール」になるため、何を作ればいいかが明確になり、不要な機能を作り込む(オーバーエンジニアリング)を防げます。Red → Green → Refactorの具体的な流れ実際のTDDサイクルを簡単な例で見てみます。「2つの数値を足す関数」を作る場合:Red: expect(add(1, 2)).toBe(3) というテストを書く。add関数はまだ存在しないのでテストは失敗するGreen: function add(a, b) { return a + b; } を書く。テストが通るRefactor: 今回は単純なのでリファクタリング不要。複雑なケースでは変数名の改善やロジックの整理をここで行うこのサイクルを数分〜十数分という短い間隔で回すのがTDDの特徴です。1時間かけて大きな機能を実装してからテストを書くのではなく、小さな単位で「テスト→実装→整理」を積み重ねます。TDDのメリットと現場での実情TDDの主なメリットは3つあります。まずバグの早期発見。テストが常にある状態で開発するため、変更によるデグレ(既存機能の破壊)にすぐ気づけます。次に設計の改善。テストしやすいコードを書こうとすると、自然と関数の責務が小さくなり、依存関係が整理されます。最後に仕様のドキュメント化。テストコード自体が「この関数は何をするか」の生きたドキュメントになります。一方で、現場では「すべてのコードにTDDを適用する」チームは少数派です。ビジネスロジックや計算処理などテストの効果が高い部分にTDDを適用し、UI層やプロトタイプ段階ではテストを後から書く、という使い分けをしているチームが多いのが実情です。TDDは「教条的に守るルール」ではなく、コードの品質と開発速度のバランスを取るための道具として捉えるのが実践的です。TDDとBDDの違いTDDと似た概念にBDD(振る舞い駆動開発)があります。TDDが関数やメソッド単位のテスト(単体テスト)を起点にするのに対し、BDDはユーザーの振る舞い(「ログインボタンを押すとダッシュボードに遷移する」など)を起点にテストを書きます。BDDはTDDの考え方をより上位の粒度に拡張したものと考えるとわかりやすく、Cucumber、Cypressなどのツールが使われます。