約70ページにもわたって MONEY EXAMPLE という事例が書かれています。 あまりに多いので一気に読むのはちょっと辛いですが、テストドリブンを夢見てがんばって読んでいます。 今回は テストドリブンの序盤となる部分をyんでみました。
実装の前にテストをすべて書くのは無理
MONEY EXAMPLE のところを見ると、 Kent Beck ですら最初にすべてのコードを書かないということがわかります。 MONEY EXAMPLE で取り上げられている単純な金額の掛け算でも、コードが徐々に完成していくのにつれてテストコードも変わっていきます。 よく、最初にテストコードを書くなんて無理だからテスト駆動開発は無理だという人がいますが、テスト駆動とはそういうことではないんですね。
MONEY EXAMPLE では、 存在しないクラスを使ってテストを書き始めます。 Red どころか コンパイルすらできないテストを書くところから始めています。 必要なクラスを考えてクラスのテストから書き始めるのかと思いましたがそうではありませんでした。 最後の理想形をテストに書いていました。
テストドリブンの手順
ToDo の整理をしてからテストを始めます。 この ToDo List はコードが完成するまでついて回ります。 載っている事例では、 存在しない Dollar
クラス を使ってテストを書くところから始めています。 そして テストを通してできるクラスのメソッドは(最初は)あくまでスタブです。
MONEY EXAMPLE は次のようにして進めていました。
- テストを書く。存在しないクラスも使ってよい。
- テストをコンパイルできるようにする。
- テストを Green にする。
- リファクタを行い、重複を取り除く。
テストを Green にする方法
- 固定値を返すなどして、テストを通るようにする。 (実装コードとしては完成ではない)
- 明らかにわかるコードを実装する。
- 複数の側面からのテストを書く。 (Triangulation)
Triangulation というのは、 テストケースを追加することで固定値での返り値を返せないようにするように、複数のパターンのテストを書くことで実装コードを規定していく方法です。 たとえば 1 から n までの総和を求める関数があったとして、 Sum(1) == 1
だけだったら Sum
の返り値は 固定の 1
でもいいですが、 Sum(5) == 15
だと実装せざるを得なくなります。 Kent Beck は本当にどのようにリファクタすればよいか迷うようなときに Triangulation を使うそうです。 どうすればいいのかわかっている場合は Triangulation のためのテストコードは書かれません。
MONEY EXAMPLE の中で出てきた Value Object というのも重要だと思うのでまとめておきます。
Value Object
一種のインスタンス変数で、コンストラクタで設定された値はそれ以降変わることがありません。 これにより、ポインタを通じた意図しない値の書き換えの発生を防ぎます。 別の言葉で書くなら 副作用対策です。
Value Object の持つメソッドの戻り値は必ず新しいオブジェクトにします。 下に例を書きました。 Num
の add
メソッドの戻り値は新しいインスタンスになっています。 これにより add
の前後で Num
のインスタンスは変更することがありません。 また、 primitive value ではないので、 別途 equal
メソッドを実装しておかないとテストコードを書くときに困ることがあります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
class Num def initialize(amount) @amount = amount end def get_amount return @amount end def add(num) return Num.new(get_amount + num.get_amount) end def equal(num) return get_amount === num.get_amount end end |
Test-Driven Development By Example の JAVA のコードをまねて作ったのですが、 言語が ruby なので実践的には get_amount
を書かずに attr_reader :amount
と書きますね。