TDD(テスト駆動開発)で変わる開発の進め方:仕様の抜け漏れを減らす手順

TDD(テスト駆動開発)で変わる開発の進め方:仕様の抜け漏れを減らす手順

はじめに

コードを書きながら、こんな不安を感じたことはないでしょうか。

「これ、本当に仕様通りに実装できているだろうか?」
「あとから考慮漏れが見つかったらどうしよう」

新卒エンジニアとして現場に出てから、私は幾度となく同じような不安を抱えながらコードを書いてきました。

特に、現在アサインされているようなスクラム開発では、最初から要件が完璧に揃っているわけではなく、スプリントを回しながら理解が深まっていくことが多いです。

そのため、実装時点での不確かさが残りやすく、手戻りへの怖さも強くなります。

スクラムなど開発手法については、この記事でも解説しています。
https://www.i3design.jp/in-pocket/11957#toc_id6

そんな中で出会ったのが、「テストを先に書く」という、これまでとは真逆の発想であるTDD(テスト駆動開発)でした。

TDD(テスト駆動開発)とは

TDD(Test-Driven-Devalopment)は、その名の通り、テストを書いてから実装を行う開発手法です。

TDDという略称で呼ばれることも多く、Kent Beck(ケント・ベック)氏によって確立されました。
現在では、日本語訳された書籍も出版されており、多くのエンジニアに知られている手法です。

ただ、私自身も最初にTDDを知ったときは、 

「テストを先に書くってどういうこと?」
「実装がまだなのに、テストを書く意味はあるの?」

と、正直あまりピンときていませんでした。

しかし、実際に調べてみると、TDDは単なるテストを書くための手法ではなく、エンジニアが迷いながらコードを書くことを防ぐための考え方だと知りました。

TDDの目的

TDDの目的は、「動作する綺麗なコード(clean code that works)を目指すこと」と言われています。

一見すると当たり前のように聞こえますが、実際の開発現場でこういった経験はないでしょうか?

  • とりあえず動くけど汚いコードになってしまう
  • 仕様が曖昧なまま実装を始めてしまう
  • リファクタリング前に、「壊してしまうのでは」と手が止まる
  • テストが後回しになる

TDDの本質は、テストを書くこと自体にあるのではなく、人間がつい陥りがちなこれらの行動を、手順で矯正してくれることにあると感じています。

TDDではまずテストを書くことで、「このコードは、どう動くことが正しいのか」を先に明確にします。

テストは、単なる確認手段ではなく、仕様そのものをコードで表現したものとも言えます。
その結果、「とりあえず動かす」実装ではなく、「どうあるべきか」を意識した実装に集中できるようになります。

また、テストという安全網があることで、リファクタリングに対する心理的ハードルが下がります。
つまり、壊れていないことを確認しながら、安心してコードを綺麗にしていくことができるのです。

TDDの手法

TDDでは、Red → Green → Refactorのサイクルで設計を改善していきます。

それぞれ簡単に説明します。

Red(赤): 失敗するテストを書く

この段階で、まだ存在しない機能に対するテストコードを書きます。

  • 目的: 満たしたい仕様(動作)を明確にする。
  • 重要なこと: テストが確実に失敗することを確認する。(これにより、テストが正しく動作すること [意図した失敗を検出できること] と、まだ実装が完了していないことを確認。)
  • ポイント: 「実装したい最小単位の機能」に焦点を当てた、最もシンプルなテストを書く。

Green(緑): テストをパスする最小限の実装

Redで失敗したテストを、成功させる(Greenにする)ための実装をします。

  • 目的: 機能が動作することを確認する。
  • 重要なこと: テストをパスさせることだけに集中し、必要最小限のコードを書く。(この時点では、コードの美しさや汎用性は気にしない。)
  • ポイント: たとえ非効率でも、テストが通るならベタ書きでも構わない。(後に続くRefactorのステップがあるため。)

Refactor(リファクタリング): コードの改善

テストがGreenであることを確認した上で、コードを綺麗にし、設計を改善します。

  • 目的: コードの品質を向上させ、保守性と可読性を高める。
  • 重要なこと: リファクタリング中も、全てのテストが常にGreenであることを保証する。(テストが安全網として機能するため、安心してコードの改善ができる。)
  • ポイント: 重複の排除、命名の改善、適切な抽象化などを慎重に行う。(改善が終わったら、再びRedのステップに戻り、次の機能の実装に取り掛かる。)

簡単な例でTDDのサイクルを実行してみる

ここからは、実際に簡単な例を使って、Red → Green → Refactorのサイクルをテストコードを書きながら回していきます。

題材として、Webアプリ内の「検索機能のバリデーション」を考えてみます。

検索フォームにキーワードを入力し、検索ボタンをクリックすると検索が実行されるという、一般的な機能を想定しています。
今回はその中でも、バリデーション部分のみに焦点を当てます。

想定する仕様

検索キーワードには、次の制約があるものとします。

  • 15文字以内
  • 15文字を超えたらエラーとする
  • 空文字のみの場合はエラーとする

この仕様をもとに、まずはRed(失敗するテストを書く)から始めていきます。

実装:Red(失敗するテストを書く)

まずは Red のステップとして、失敗するテストを書いていきます。
この時点では実装はまだ存在しません。
そのため、これらのテストは全て失敗する状態からスタートします。

今回は、TDDの流れを説明するために、シンプルな仮想の例を用いています。

テストを書いていくと、「入力値の前後に空白が含まれている場合をどう扱うべきか?」という論点が自然と浮かび上がってきます。

このようにTDDでは、テストを書く過程そのものが、仕様の抜けや曖昧さを整理する手助けになります。

実装:Green(テストをパスする最低限の実装)

次に Green のステップとして、テストを通すための最低限の実装を行っていきます。

ここでは、「正しく設計すること」よりも、「まずテストを通すこと」を優先します。

この時点では、

  • 条件分岐がそのまま並んでいる
  • 仕様の意図がコードから即座に読み取りづらい

など、決して「綺麗な実装」とは言えません。
しかし、重要なのは全てのテストが通る状態を先に作ることです。

上記のテスト結果から、書いたテストが全て通り、Greenのステップが完了していることが確認できます。

テストという安全網が張られたことで、次のステップである Refactor では、安心して設計や可読性の改善に取り組むことが可能になりました。

実装:Refactor(コードの改善)

最後に、Refactorのステップでは、振る舞いを一切変えずに、可読性と意図の明確さを改善していきます。

今回のRefactorでは、ロジック自体に大きな変更点はありません。

主な変更点は以下の通りです。

  • マジックナンバーだった”15”を定数として切り出す
  • 「何をチェックしているか」をコメントと構造からわかるようにする

このように、Refactorは必ずしも大きな書き換えを行うものではありません。

テストがあることで、「動作を変えてしまっていないか」を気にすることなく、安心して小さな改善を積み重ねることができます。

Refactor の結果、見た目の変化は小さいかもしれませんが、「なぜこのコードがこうなっているのか」は、Green の時よりも明確になりました。

では、コードを自動で書いてくれるAIが登場した今でも、TDDの価値は変わらず意味を持つのでしょうか。

AI時代のTDD

AIがコードを書いてくれたり、実装をナビゲートしてくれる場面が増えた今でも、TDDの本質は変わらないと私は考えています。

TDDは、テストを書くことで仕様を明確にし、そのテストに守られた状態でリファクタリングを進めていくための手法です。

AIは、このプロセスをより効率的にしてくれます。

実装コードの生成や、テストを書いていく中での観点の洗い出しなど、TDDの各ステップを強力にサポートしてくれる存在です。

ただし、

「何を仕様として定義するのか」
「その仕様が本当に正しいのか」

を判断する部分までを、AIに丸投げはできません。

最終的に必要なのは、人間が仕様と向き合い、テストという形でそれを言語化し、改善を重ねていくプロセスそのものだと考えています。

AIを補助役として活用しながら、TDDという枠組みを使って開発を進めることで、より安心して、より速く、より質の高いコードを書いていけるのではないかと感じています。

まとめ

TDDはテストを書くことで仕様を明確化し、安全な環境でリファクタリングを可能にする開発手法です。

これは単に「テストのための手法」というよりも、エンジニアが安心して実装・改善できるプロセスそのものといえます。

Red→Green→Refactorのサイクルを回すことで、判断の迷いや仕様の見落としといった、人間がつい陥りがちな失敗を、手順として矯正してくれます。

実際、今回の例でも、テストを書き始めるまでは「入力値を trim する必要がある」という観点に気づけませんでした。

経験を積んでいれば自然に考慮できることでも、新卒や経験の浅いエンジニアにとっては、まだ引き出しとして持っていないケースも多いはずです。

経験が十分でなくても、TDDという枠組みがあることで、自分の判断を1つずつ確認しながら進められています。
新卒の今だからこそ、その価値を強く感じています。

経験の浅さに不安を感じている人や、「これで合っているのか」と迷いながら実装している、同じ不安を抱えた人たちの支えになる考え方の1つとして、参考になれば嬉しいです。

レガシーな環境から転職したエンジニアが、「AI活用」によって感じた変化
サムネイル