自動販売機の概略
粒度を小さくするために「自動販売機」クラスをさらに詳細にモデル化したいわけですが、幸いなことに自動販売機には実物があります。実際の自販機の構造は、だいたい次の図のようになっています。
投入されたお金を勘定したり釣り銭を出したりするユニットは、コインメック(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 払戻金;
}
