若くない何かの悩み

何かをdisっているときは、たいていツンデレですので大目に見てやってください。

私の TDD の理解と Kent Beck による TDD の解説の比較

TDD(テスト駆動開発)の提唱者 Kent Beck による TDD の定義の解説を @t_wada さんが翻訳したブログが公開されました。

t-wada.hatenablog.jp

ここで解説されている TDD と私のこれまで理解していた TDD(後述)を比較します。 みなさんの TDD の理解もぜひ知りたいので自身の思う TDD のプロセスを教えてください。

TL;DR

概ね一致していたようです。細かい差異としては Kent Beck は後述する PFD の「脳内の SUT 仕様(D10)」を生成するアクティビティも TDD に含まれていると主張しているようなので、仕様を分析してテストシナリオを洗いだすアクティビティを D10 の前に追加するとよさそうですね。

私の理解していた TDD

PFD(Process Flow Diagram) で表現します。

PFD

TDD のプロセスの PFD 表現

緑色の成果物はテストが成功する成果物であることを意味し、赤色の成果物はテストが成功しない成果物であることを意味します。

要素表

成果物要素表

アクティビティID アクティビティの説明
A1 脳内のテスト対象(SUT)の仕様から最も実装の容易な入出力の組を選び、これを最初のテストケースとしてテストに追加する。
A2 空のSUTのままテストを実行し失敗することを確認する。これには失敗すべきときに実際にテストが失敗することを確認する意図がある。
A3 D6のテストを成功させる最も単純な実装をSUTに実装する。期待される出力をそのままreturnするので構わない。この作業はFakeItと呼ばれる。テストが成功すべきときに実際に成功することを確認する意図がある。
A4 テストが失敗であればD7へのテストが通るようにテストを修正する。もし修正しなくともテストが成功するようであれば何もしない。
A5 脳内仕様から同値パーティションの代表値や境界値となる入力を選んでテストケースとして実装しテストに追加する。同値パーティションの代表値を入力に選ぶ場合はテストケースの名前に同値パーティションの名前を設定する。境界値を選ぶ場合はその境界値を選んだことがわかるような名前をテストケースの名前として設定する。この時点ではまだSUTを編集してはいけない。
A6 テストを実行する。
A7 テストが成功するまで最も素早く実装できる方法でSUTを編集する。もし最初からテストが成功している場合は何もしない。この時点ではコードの簡潔さを気にしない。
A8 SUTまたはテストのいずれか一方(Xとする)だけを簡潔する。もしテストが失敗する場合はテストが成功するようになるまでXを修正する。これをリファクタリングという。

プロセス要素表

成果物ID 成果物の説明 実行可能なリソース
D0.0 仕様のサンプルである全てのテストケースにおいて実際の出力が期待する出力と一致するテスト対象(SUT)。SUTの実装は簡潔であり保守性が高い。サンプル数が十分であれば仕様を満たしたテスト対象の実装と見なしてよい。 実装者
D0.1 仕様のサンプルである入出力の組のすべてが実際にSUTから得られるか確かめるコード。実装が簡潔であり保守性が高い。もし1つでもSUTからの出力が仕様から期待される出力と異なるならば全体として失敗を返す。そうでなければ全体として成功を返す。 実装者
D1 仕様のサンプルである全てのテストケースにおいて実際の出力が期待する出力と一致するSUT。SUTの実装は簡潔であるとは限らない。サンプル数が十分であれば仕様を満たしたテスト対象の実装と見なしてよい。 実装者
D2 追加されたテストケース以外は成功するテスト。追加されたテストケースだけは成功・失敗のどちらでもよい。 実装者
D3 仕様のサンプルである入出力の組のすべてが実際にSUTから得られるか確かめるコード。テストの実装が簡潔であるとは限らない。もし1つでもSUTからの出力が仕様から期待される出力と異なるならば全体として失敗を返す。そうでなければ全体として成功を返す。 実装者
D4 仕様のサンプルであるテストケースにおいて実際の出力が期待する出力と一致するとは限らないSUT。SUTの実装は簡潔であるとは限らない。 実装者
D5 仕様のサンプルとして選ばれた1つの入出力の組が実際にSUTから得られるか確かめるコード。もしSUTからの出力が仕様から期待される出力と異なるならば全体として失敗を返す。そうでなければ全体として成功を返す。 実装者
D6 仕様のサンプルとして最初に選ばれた入出力の組が実際にSUTから得られるか確かめるコード。もしSUTからの出力が仕様から期待される出力と異なるならば全体として失敗を返す。そうでなければ全体として成功を返すべきだがそうなっているとは限らない。 実装者
D7 仕様のサンプルとして最初に選ばれた入出力の組が実際に帰ってくるSUT。SUTの実装は期待する出力をreturnで返す程度に極めて単純であるべきである。 実装者
D8 何も実装されていないテスト。 実装者
D9 何も実装されていないSUT。 実装者
D10 実装者の脳内にあるこれから実装したいコンポーネントの仕様。テストケースを追加するうちに浮き彫りにできるので明確である必要はこの時点ではない。 実装者

終わりに

みなさんの TDD の理解もぜひ知りたいので自身の思う TDD のプロセスを教えてください!