Note: 社内のTDDを知らない人向けに、TDDの基本説明とデモを行なう機会があり、その時に使った資料です。
テスト駆動開発(TDD)では、「動作するきれいなコード」を維持しながら機能追加を行なうことを目指します。
- 今から実装してようとしている小さい問題について、TODOリストを作成する。
- 一番取り掛かりやすいTODOを選択し、テストを行なうメソッドを追加する。
- テストを実行して、テスト結果が失敗することを確認する。🔴
- 考えられる一番簡単な実装を行なう。
- テストを実行して、テスト結果が成功することを確認する。🟢
- 実装に改善できる箇所があればリファクタリングを行なう。
- テストを実行して、テスト結果が成功することを確認する。🟢
これをループで回しながらTODOリストを消化していきます。 このループは、「レッド → グリーン → リファクタリング」と表現されます。 実装を進めていくと、要件が明確になることで、追加のTODOが出てきます。 追加のTODOは、TDDのループの実践からの貴重なフィードバックなので、TODOリストに追加します。 レッドの状態が長く続いてしまう場合は、TODOの粒度が大きすぎることが原因かもしれません。 それがストレスになるようであれば、TODOを更に分割できないか検討してください。
このループに忠実に従うことは、一見まどろっこしいと思う人もいるかもしれませんが、多くの利点があります。
TODOリストを作成することは、今から実装する処理についての設計を行なうことになります。 また、TODOリストはテストメソッドに反映されることになるため、動作する設計書の役割を担うことができます。
開発者は、大量の項目の処理や複雑な処理を実装する場合、既存の機能を壊すことなく実装するために、大きなストレスを感じながら作業することになります。 レッド → グリーン → リファクタリングのループでは、TODOリストの一つのTODOに専念して行なうため、ストレスが少なくなります。 更に、レッド、グリーンを小まめに確認しながら進めるため、グリーンの状態の時に常に「希望通りに動作する」ということに自信を持てることもストレスを軽減します。
テストコード: テストが記載されたファイル プロダクションコード: 本番環境で動作する実装が記述されたファイル
テストコードは、初期の実装時だけでなく、長い間様々な用途で役に立ちます。
TDDで開発されたテストコードは、開発時のTODOリストを反映したものとなっているので、どのように使用されることを想定しているか。というのを確認するときに参考になります。
TDDで作成した既存のコードは、テストコードが残っているため、すぐTDDループで開発を行なうことができます。 具体的には、まず仕様変更に従ってテストコードを修正し、レッド🔴を確認してから、プロダクションコードを修正して🟢を確認します。 テストコードが継続的に利用可能であることが、既存の機能を壊さずに仕様変更を行うためのガードレールとなります。
不具合調査時に、想定外の使われ方をしたために発生したのか、実装が間違っていたのかなど原因の調査時に、テストコードは役に立つ情報となります。
全てのテストを実行してグリーンを保っているのであれば、実装時に想定していた通りの動作が現在も機能していることを確認できます。 デプロイ前にリグレッションテストとして実行することで、既存の機能を壊していないかどうかを確認できると安心してデプロイできるようになります。(TDDのテストだけで100%安心とはなりませんが、不安を減らすことができます)
手順はわかっても、上手にTDDループを回し、ストレスなく継続的に運用していくのは、なかなか難しいというのも事実です。
テスト対象のコンポーネントの特定は、最初のかなり重要な設計の勘所です。
どういう要素が絡むとテストを書くのが難しくなるかを理解する必要があります。
ユーザー入力、ファイル入出力、データベースクエリ、API呼び出しなどを内包しているコンポーネントは、テストを書くのが難しくなります。
ランダムな数値を取得する関数などを呼び出すメソッドは、テスト結果もランダムになるため、テストを書き難くします。
rand関数(乱数を生成する)の呼び出し- 現在時刻の取得
副作用の無いコンポーネントを設計することができれば、テストを書くのは非常に簡単になります。