🚥

テスト駆動開発(TDD:Test-Driven Development)を学ぶ

に公開

はじめに

テスト駆動開発(TDD:Test-Driven Development) という存在は知っていましたが、私は今までTDDを学んだことがありませんでした。

この記事は、最近学びだしたTDDについて書くことで理解を深めよう というものです。

テスト駆動開発(TDD:Test-Driven Development)とは?

まず、人によって TDD という言葉が指しているものが異なっているせいで、TDDの認識に混乱が見られるみたいです。

そもそも、テスト駆動開発(TDD:Test-Driven Development)とは何なのでしょうか?
参考にした記事には以下のように書かれていました。

テスト駆動開発(TDD: Test-Driven Development)はプログラミングのワークフローだ。あるプログラマが、あるシステム(まだ無いかもしれないが)の振る舞いを変更する必要があるとする。TDDの狙いは、そのプログラマを支援して、システムを下記のような新たな状態に導くことだ。

・それまで動作していたものは引き続き全て動作する
・新しい振る舞いは期待通りに動作する
・システムはさらなる変更の準備ができている
・プログラマとその同僚は、上記の点に自信を持っている

要するに、TDD とは プログラミングのワークフロー のことです。
テストファースト(テストコードを実装よりも前に書くこと)の利点を伸ばし、欠点を補うために生まれたみたいです。

TDDの特徴

テストファーストの強みを活かしつつ、保守性を上げるためにリファクタリングが組み込まれている。

TDDがもたらす効果

明確なプロセス
TDDのメリットは 目の前への集中 です。
TDDのサイクル(後に解説するイテレーティブな開発手順)では、各ステップで何をやるべきかが明確です。今自分がいるステップを認識していれば、今自分が何をすべきかが明確になります。

  • レッド:仕様をテストコードで表現することに集中(あるべき設計を定義⁠)⁠
  • グリーン:失敗しているテストを成功させることに集中(動作するコード⁠)
  • リファクタリング:動作しているコードをきれいにすることだけに集中(きれいなコード⁠)⁠

TDDの開発手順

TDDの開発手順はイテレーティブ(繰り返しながら作る)です。
TDDは「レッド・グリーン・リファクタリング」といったサイクルで開発されます。
次のようなサイクルです。

  1. やるべきことを箇条書きでまとめ、ToDo形式で書き出しておく(テストリスト)
  2. レッド:ToDoリストから「1つ」ピックアップし、テストから書き(テストファースト⁠)⁠、そのテストを実行して失敗させる
  3. グリーン:失敗しているテストを成功させることに集中した実装を行う(テストを通す最小限のコード、綺麗より動作優先のコード。⁠)
  4. リファクタリング全てのテストが成功している状態(テストは通ったままにしておく必要がある) で、実装コード、テストコードをリファクタリング(整理整頓)する
  5. リファクタリングが終わったら、気付きをToDoリスト(テストリスト)に反映し、次のToDoを選んでまたレッドに戻る

このように レッド→グリーン→リファクタリングの手順を繰り返す開発がTDD です。

インクリメンタルな設計(段階的な開発)

  • TDDは、一度に1つだけのことを行います
  • 最初に全ての設計を終わらせてから実装に入るのではなく、小さく安全なステップを繰り返しながら、「設計」「実装」「テスト」の全てを1つずつ積み上げていくように、段階的に開発していきます
  • 「⁠考えすぎ」「⁠やりすぎ」を未然に防ぐために、必要だと思うものから着手します
  • 設計を常に振り返り、必要十分な設計を得たら、それ以上の深追いはせず、現状の設計とコードをきれいに保つことに努めます

TDDは、

  • 「テストファースト」と「リファクタリング」を技術的な基盤に据えてる
  • 「イテレーティブな開発手順」と「インクリメンタルな設計」を組み合わせてる

このように、無駄がなく規律のある反復型のプログラミング手法 です。

良い設計とは?

TDDの提唱者:Kent Beck氏によると、

  1. テストが成功している
  2. 意図が明確に表現されている
  3. 重複がない
  4. 最小の要素で構成されている

とのことです。TDDは良い設計に近づく準備 になります。

"それら" は何と呼ぶのか?

TDDの認識の誤りを無くすために、TDDと呼ばれてしまっている "それら" の認識を以下のように整理します。

" 自動テスト(Automated Test)"

  • テスティングフレームワークを使ってテストコードを書くこと
  • 開発対象のコードに対するテストもコードとして書き(テストコード⁠)⁠、そのコードを実行することで、テストの実施を自動化する取り組みのこと
  • 誰が書いても構わないし、いつ書いても構わないもの。

自動テストが必ず満たすべき性質(必須性質)

自己検証可能

  • テストには成功か失敗かの2つの結果しかないこと。そして、テスト自身が人間の目を介さずに成功/失敗を判断できること

繰り返し可能

  • テストがいつでもどこでも同じように動くようにすること
  • 自動テストは、人の手を介さず、実行するたびに毎回同じように動かなければいけない。テスト実行後に人の手が加わっているのであれば、それは 繰り返し可能 ではない。

自動テストが持つべき性質(推奨性質)

独立であること

  • あるテストが、他のテストに影響を及ぼしてない状態のこと。
  • 自動テストは互いに独立していることが望ましい。

高速であること

  • 自動テストは高速でないと実行頻度が下がり、その結果、問題の把握が遅れることで自信が無くなり、コードの変更に躊躇するようになってしまう。
  • 自動テストは可能な限り高速に動くことが望ましい。

自動テストの理想的な状態

  • テスト対象を実装する開発者によって実装と近いタイミング(ほぼ同時)で書かれている
  • 対象範囲が無駄なく漏れなくカバーされている
  • 頻繁に実行されている

" 開発者テスト(Developer Testing)"

  • 開発者が自分でテストコードを書くこと
  • 開発者自身が自動テストを書きながら開発すること
  • テストコードを書くタイミングは後からでも構わないが、実装を行うタイミングに近ければ近いほど効果が高まるもの。

" テストファースト(Test-First Programming)"

  • テストコードを実装よりも前に書くこと
  • ⁠時間が経過するとテストを書く難易度が上がる という問題があり、それなら「実装後じゃなくて、実装前にテストを先に書いたらいいんじゃない?」という観点で始まったのが、テストファーストの考え方みたいです。

テストファーストがもたらす効果

必ずテスト可能なコードになる

  • 面倒なテストを後回しにしてしまうと、結局書かれないことが多い。それならば先にテストを書いてしまって、対象コードは必ずテスト可能にするという方法。

インターフェースと実装を分けて考えられる
テストファーストでは、

  1. それは何なのか、どうあるべきなのか?
  2. それをどう作るのか?

が考える順番になります。(振る舞い、仕様、仮説 などと表現するみたいです)
テストファーストの場合、実装が存在しないので自然とインターフェースから先に考えることができます。

利用者の視点に立った設計
テスト対象の最初の利用者は「テストコード」です。
テストファーストの場合、

  1. どう使えるとうれしいか?
  2. それをどう作るか?

という順番で考えるようになります。
ソフトウェア設計において重要なのは、利用者にとって「使いやすい」「分かりやすい」という点です。
テストファーストでは、まだ存在しない実装を先に利用することで利用者の視点に立つことができます。

これから

Claude CodeでTDDをしてみます。
手を動かし、TDDの理解を深めていこうと思います。

TDDを学ぶための書籍

https://amzn.asia/d/7rBckTV

参考

https://t-wada.hatenablog.jp/#ステップ2-ひとつテストを書く

https://gihyo.jp/article/2024/01/automated-test-and-tdd

Discussion