はじめに
TDDの基礎を説明する時、簡単な例をということでどうしても状態に依存しないメソッドから始めてしまいます。この連載でも、前回まではそういったシンプルなメソッドだけを扱ってきました。そろそろ状態を持ったオブジェクトを、と考えていた矢先にTDDBC大阪で素晴らしい課題が出されたので、それを使わせていただくことにしました。
といっても、TDD自体にはクラス設計の技法やルールは含まれていません。どのようにクラス設計を進めていくかは、TDDの観点からは自由です。ただし、並行して進めていくことで、テスタビリティに優れたクラス設計になるだろう、とは言えます。この記事でのクラス設計の方法は、あくまでも1つの例であるとご理解いただければ幸いです。
対象読者
- TDDに興味をお持ちの.NET Frameworkの開発者。
必要な環境
サンプルコードを試してみるには、C# 2010(Expressで可)とNUnit 2.6が必要です。本稿執筆時点では、下記から入手できます。
- C# 2010 Express: Microsoft Visual Studio Express
- NUnit 2.6: NUnit V2 2.6.0
- NUnitのインストール手順: NUnit 2.5 の導入 Step by Step(筆者サイト、旧バージョンでの説明ですが基本的に同じです)
※Visual Studio 2012 RCでも構いません。その場合は、Visual StudioのIDEとNUnitを統合できます(「Visual Studio 11 betaの単体テスト機能を使ってみよう!」を参照)。
TDDBC大阪の課題「飲み物自動販売機」
この課題は、自販機の組み込みソフトを作ろうというのではありません。いわば自販機のシミュレーターを作ってみよう、というものです。ここでは、2日目の課題を使います。全文は、TDDBCのWikiをご覧ください。ここではとりあえず課題のタイトルだけを抜き書きしておきます。
- ステップ0 お金の投入と払い戻し
- ステップ1 扱えないお金
- ステップ2 ジュースの管理
- ステップ3 購入
- ステップ4 機能拡張
- ステップ5 釣り銭と売り上げ管理
どんなクラスを作ればよいでしょう?「自動販売機」クラスが必要になるのは分かります。いきなりステップ3「購入」やステップ5「釣り銭と売り上げ管理」のテストケースを書いて、「自動販売機」クラスを作ってしまえばいいじゃないか、という攻め方でも可能ではあるでしょう。しかし、おそらくそれでは粒度が大きすぎます。テストケースは複雑になり、またケース数も爆発し、設計を考えることがどんどん困難になっていってしまうでしょう(注1)。「自動販売機」クラスを、もう少し粒度の小さなクラスに分けて進めていくと、楽に作っていくことができます。
このTDDBC大阪の課題の素晴らしい点の1つは、ステップ0~ステップ2を設けることで、「自動販売機」クラスよりも小さなクラスの存在を示してくれているところにあります。
ちなみに、ステップ5まで完成したプログラムのサイクロマティック複雑度を計測すると、67でした。課題の全てを「自動販売機」クラス1つに作り込んだとすると、それをテストする「自動販売機Test」クラスに含まれるテストケースの数は、その程度かそれよりも多くなるでしょう。数十個ものテストケースの整合性を常に考えていられるでしょうか?