自動販売機の概略
粒度を小さくするために「自動販売機」クラスをさらに詳細にモデル化したいわけですが、幸いなことに自動販売機には実物があります。実際の自販機の構造は、だいたい次の図のようになっています。
![飲み物自動販売機の概略 飲み物自動販売機の概略](http://cz-cdn.shoeisha.jp/static/images/article/6633/fig01.png)
投入されたお金を勘定したり釣り銭を出したりするユニットは、コインメック(Coin Mechanism、硬貨用)やビルバリ(Bill Validator、紙幣用)と呼ばれています。商品ラックは複数のビンを持っていて、それぞれにジュースを格納しています。そして、それらの動きを制御するコントローラーがあります(実際のコントローラーは複数の基板に分かれています)。
設計方針としては、コインメック(ビルバリの機能も含む)・商品ラック・コントローラーを個別に作り、最後に自動販売機として組み立てることにしましょう。
ステップ0 お金の投入と払い戻し
これはコインメックだけの機能です。お題を見ていきましょう。
ステップ0 お金の投入と払い戻し
- 10円玉、50円玉、100円玉、500円玉、1000円札を1つずつ投入できる。
- 投入は複数回できる。
- 投入金額の総計を取得できる。
- 払い戻し操作を行うと、投入金額の総計を釣り銭として出力する。
それでは、テストファーストしていきます。なお、製品コードとテストコードは別々のプロジェクトに分けます。製品コード側のAssemblyInfo.csにはInternalsVisibleTo
属性を追加して、テストコード側から製品コード中のInternal
なものにもアクセスできるようにしておきます。
InternalsVisibleTo
属性を追加
[assembly: InternalsVisibleTo("Tddbc大阪2Test")] // "Tddbc大阪2Test"は、テストコードのアセンブリ名
ステップ0 1,3: コインメックにお金を1回だけ投入する
課題文には「投入金額」という言葉が出てきますが、この金額はジュースを提供すると減ります。まだ代金として受け取ってはいないお金ということで、「預り金」と呼ぶことにします。
まずは、10円玉を入れたら預り金が10円になる、という仮実装から始めましょう。ステップ0の1番目(の一部)と3番目をいっぺんにやります。
[TestCase()] public void お金を1回だけ投入するTest() { コインメック cm = new コインメック(); cm.お金を投入する(10); Assert.AreEqual(10, cm.預り金); }
お金を投入する()
とコインメック
の預り金
という状態が変化するわけですね。メソッド呼び出しの結果として状態が変化する場合は、このように状態の変化をアサートします。
このテストケースを満たす仮実装は、次のようになります。
コインメック
クラス
internal class コインメック { public int 預り金 { get { return 10; } } public void お金を投入する(int coin) { } }
テストコードをリファクタリングしてから、50円のテストケースを追加します。三角測量によって、お金を投入する()
メソッドと預り金
プロパティを完成させます。
[TestCase(10, 10)] [TestCase(50, 50)] // ←追加 public void お金を1回だけ投入するTest(int 投入金, int 預り金の期待値) { コインメック cm = new コインメック(); cm.お金を投入する(投入金); Assert.AreEqual(預り金の期待値, cm.預り金); }
コインメック
クラス
private int _deposit; public int 預り金 { get { return _deposit; } private set { _deposit = value; } } public void お金を投入する(int coin) { 預り金 = coin; }
なお、ここでは残りの金種(100円、500円、1000円)のテストケースを書いていません。なぜなら、それらのテストケースを追加しても、REDにできないからです。受け付ける金種については、ステップ1で実装することになります。
ステップ0 2,3: コインメックにお金を複数回投入する
続けてお金を投入したら、その合計が預り金の額になるはずです。テストケースを1つ追加して、お金を投入する()
メソッドを修正すれば完了です。
[TestCase] public void お金を複数回投入するTest() { コインメック cm = new コインメック(); cm.お金を投入する(100); cm.お金を投入する(500); Assert.AreEqual(600, cm.預り金); }
コインメック
クラス
public void お金を投入する(int coin) { 預り金 += coin; }
ステップ0 4: コインメックの払い戻し操作
ステップ0の4番目は、お金を払い戻す()
メソッドを実装します。投入した合計金額が返ってくるとともに、預り金
は0にならなければいけません。
預り金
は0になる
[TestCase] public void お金を払い戻すTest() { コインメック cm = new コインメック(); cm.お金を投入する(1000); var 払戻金 = cm.お金を払い戻す(); Assert.AreEqual(1000, 払戻金); Assert.AreEqual(0, cm.預り金); }
コインメック
クラス
public int お金を払い戻す() { var 払戻金 = 預り金; 預り金 = 0; return 払戻金; }