Eclipseを起動したらテストを実行する。
「水を買う」でエラーとなっていることがわかるので、そこから作業を再開する。
100円を入れても水を買えないのは、canBuy()がコーラに対応するコードしか書かれていないため、正しく判定できていないから。
まずは、コーラの場合だけをif文で分けてみる。
public boolean canBuy(String name) { if (name.equals("コーラ")) { if (colas.size() == 0) return false; if (total < 120) return false; } return true; }
お金を入れなくてもレッドブルと水を買えるようになってしまうので、水を買うところにテストを追加して買えないようにする。
@Test public void 水を買う() { VendingMachine vm = new VendingMachine(); Juice j = vm.buy("水"); assertNull(j); vm.putMoney(100); j = vm.buy("水"); assertThat(j.getName(), is("水")); }
コーラと同様に、レッドブルと水についてもif文を追加する。
public boolean canBuy(String name) { if (name.equals("コーラ")) { if (colas.size() == 0) return false; if (total < 120) return false; } if (name.equals("レッドブル")) { if (redbulls.size() == 0) return false; if (total < 200) return false; } if (name.equals("水")) { if (waters.size() == 0) return false; if (total < 100) return false; } return true; }
テストにパスするようになる。
投入金額と在庫数をもとに、購入可能なドリンクのリストを取得できるようにする。
新しく作成するメソッドの仕様を考える必要がある。
購入可能ということなので、既存のcanBuy()メソッドのオーバーロードにする。
戻り値はJuiceの配列で、引数なしとする。
public Juice[] canBuy() { return null; }
テストを作成する。
初期状態ではお金を投入していないので、何も購入できない。
@Test public void 購入可能なドリンクのリスト() { VendingMachine vm = new VendingMachine(); Juice[] j = vm.canBuy(); assertThat(j.length, is(0)); }
テストにパスするように修正する。
public Juice[] canBuy() { return new Juice[0]; }
100円を入れると水を買えるようになる。
@Test public void 購入可能なドリンクのリスト() { VendingMachine vm = new VendingMachine(); Juice[] j = vm.canBuy(); assertThat(j.length, is(0)); vm.putMoney(100); j = vm.canBuy(); assertThat(j.length, is(1)); assertThat(j[0].getName(), is("水")); }
テストにパスするように修正する。
public Juice[] canBuy() { if (total < 100) return new Juice[0]; Juice j = new Juice(); j.setName("水"); Juice[] ja = new Juice[1]; ja[0] = j; return ja; }
さらに50円を投入すると、水とコーラが買えるようになる。
@Test public void 購入可能なドリンクのリスト() { VendingMachine vm = new VendingMachine(); Juice[] j = vm.canBuy(); assertThat(j.length, is(0)); vm.putMoney(100); j = vm.canBuy(); assertThat(j.length, is(1)); assertThat(j[0].getName(), is("水")); vm.putMoney(50); j = vm.canBuy(); assertThat(j.length, is(2)); }
フェイクコードでは困難なので、投入金額をもとにJuice配列を生成する。
さらに売切れの場合も考慮したテストを作成する。
@Test public void 購入可能なドリンクのリスト売切あり() { VendingMachine vm = new VendingMachine(); vm.putMoney(1000); Juice[] ja = vm.canBuy(); assertThat(ja.length, is(3)); for (int i = 0; i < 5; i++) { Juice j = vm.buy("水"); } ja = vm.canBuy(); assertThat(ja.length, is(2)); }
すでに在庫と投入金額をもとに買えるかどうかを調べられるcanBuy()があるので、それを利用する。
public Juice[] canBuy() { Juice[] juices = getJuiceVariety(); List<Juice> list = new ArrayList<>(); for (Juice j : juices) { if (canBuy(j.getName())) { list.add(j); } } return list.toArray(new Juice[0]); }
リファクタリングの例
Juiceに引数付きコンストラクタを追加する。
package tdd; public class Juice { private String name; private int price; public Juice() {} public Juice(String name, int price) { this.name = name; this.price = price; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getPrice() { return price; } public void setPrice(int price) { this.price = price; } }
VendingMachineのコンストラクタを短く書き換えられるようになる。
public VendingMachine() { for (int i = 0; i < 5; i++) { colas.add(new Juice("コーラ", 120)); redbulls.add(new Juice("レッドブル", 200)); waters.add(new Juice("水", 100)); } }
buy()メソッドのリファクタリング例。
public Juice buy(String name) { if (!canBuy(name)) return null; List<Juice> list = null; if (name.equals("コーラ")) { list = colas; } if (name.equals("レッドブル")) { list = redbulls; } if (name.equals("水")) { list = waters; } if (list != null) { Juice j = list.remove(0); amountSold += j.getPrice(); total -= j.getPrice(); return j; } return null; }
getJuiceVariety()のリファクタリング例。
public Juice[] getJuiceVariety() { Juice[] j = new Juice[3]; j[0] = new Juice("コーラ", 120); j[1] = new Juice("レッドブル", 200); j[2] = new Juice("水", 100); return j; }