前回は、以下のテストを書いたところで終了した。。
@Test
void ジュースを3種類管理できる() {
VendingMachine vm = new VendingMachine();
vm.put(100);
boolean b = vm.canBuy("水");
assertThat(b, is(true));
b = vm.canBuy("コーラ");
assertThat(b, is(false));
b = vm.canBuy("レッドブル");
assertThat(b, is(false));
}
テストを実行してエラーになることを確認する。
水を購入できるか問い合わせたところでエラーが発生。
canBuy() メソッドでは、引数で名前をもらっているけど参照していなかった。
ジュースの名前を見て購入可能かを判断するように修正する。
public boolean canBuy(String name) {
if (juices.size() == 0) return false;
return total >= getJuicePrice(name);
}
getJuicePrice()にも引数を追加して、ジュースの名前を見て価格を返すようにする。
public int getJuicePrice(String name) {
if (name.equals("水")) return 100;
if (name.equals("コーラ")) return 120;
return 200;
}
3種類のジュースを購入できるようにしたいので、水を購入するテストを追加する。
@Test
void ジュースを3種類購入する() {
VendingMachine vm = new VendingMachine();
vm.put(1000);
Juice j = vm.buy("水");
assertThat(j.getName(), is("水"));
}
これまで、1種類(コーラ)しか売ってなかった自動販売機を複数に対応させるのは割と大変。
まずは3種類のジュースを在庫できるようにする。
Mapを追加して、ジュースの名前で在庫を取得できるようにする。
public class VendingMachine {
private int total;
private int sales;
private Map<String, List<Juice>> map = new HashMap<>();
private List<Juice> juices;
public VendingMachine() {
total = 0;
juices = new ArrayList<>();
map.put("水", juices);
for (int i = 0; i < 5; i++) {
juices.add(new Juice("水", 100));
}
juices = new ArrayList<>();
map.put("コーラ", juices);
for (int i = 0; i < 5; i++) {
juices.add(new Juice("コーラ", 120));
}
juices = new ArrayList<>();
map.put("レッドブル", juices);
for (int i = 0; i < 5; i++) {
juices.add(new Juice("レッドブル", 200));
}
}
テストを実行すると、これまで動いていたテスト「ジュースを購入する()」などでエラーになる。
これはジュースの名前を見ていないのが原因。
ジュースの名前を見て、Mapから在庫を取得するように変更する。
public Juice buy(String name) {
if (!canBuy(name)) return null;
juices = map.get(name);
Juice j = juices.remove(0);
total -= j.getPrice();
sales += j.getPrice();
return j;
}
3種類のジュースを購入するテストを書く。
@Test
void ジュースを3種類購入する() {
VendingMachine vm = new VendingMachine();
vm.put(1000);
Juice j = vm.buy("水");
assertThat(j.getName(), is("水"));
int total = vm.getTotal();
assertThat(total, is(900));
int stock = vm.getJuiceStock("水");
assertThat(stock, is(4));
j = vm.buy("レッドブル");
assertThat(j.getName(), is("レッドブル"));
total = vm.getTotal();
assertThat(total, is(700));
j = vm.buy("コーラ");
assertThat(j.getName(), is("コーラ"));
total = vm.getTotal();
assertThat(total, is(580));
int sales = vm.getSales();
assertThat(sales, is(420));
}
getJuiceStock() に引数を追加して、ジュースの名前を指定して在庫を取得できるようにする。
public int getJuiceStock(String name) {
return juices.size();
}
ジュースの名前を見てないのに、getJuiceStock() のテストをパスしているので、怪しい実装を修正するためにテストを追加する。
@Test
void ジュースを3種類購入する() {
VendingMachine vm = new VendingMachine();
vm.put(1000);
Juice j = vm.buy("水");
assertThat(j.getName(), is("水"));
int total = vm.getTotal();
assertThat(total, is(900));
int stock = vm.getJuiceStock("水");
assertThat(stock, is(4));
stock = vm.getJuiceStock("レッドブル");
assertThat(stock, is(5));
j = vm.buy("レッドブル");
assertThat(j.getName(), is("レッドブル"));
total = vm.getTotal();
assertThat(total, is(700));
j = vm.buy("コーラ");
assertThat(j.getName(), is("コーラ"));
total = vm.getTotal();
assertThat(total, is(580));
int sales = vm.getSales();
assertThat(sales, is(420));
}
このテストで、レッドブルの在庫数を正しく返せてないことがわかる。
getJuiceStocck() をきちんと実装しなおす。
public int getJuiceStock(String name) {
juices = map.get(name);
return juices.size();
}
購入可能なドリンクのリストを知りたい!
テストを作成する。
@Test
void 購入可能なドリンクのリスト() {
VendingMachine vm = new VendingMachine();
Juice[] list = vm.getBuyableJuices();
assertThat(list.length, is(0));
}
コンパイルエラーを解消する。
VendingMachine に以下のメソッドを追加する。
public Juice[] getBuyableJuices() {
return null;
}
テストには失敗するので、テストにパスするように実装する。
とりあえず fake で。
public Juice[] getBuyableJuices() {
return new Juice[0];
}
テストを追加する。
100円を投入すると水を購入できる。
@Test
void 購入可能なドリンクのリスト() {
VendingMachine vm = new VendingMachine();
Juice[] list = vm.getBuyableJuices();
assertThat(list.length, is(0));
vm.put(100);
list = vm.getBuyableJuices();
assertThat(list.length, is(1));
assertThat(list[0].getName(), is("水"));
}
100円を追加投入すると3種類のドリンクを購入できる。
@Test
void 購入可能なドリンクのリスト() {
VendingMachine vm = new VendingMachine();
Juice[] list = vm.getBuyableJuices();
assertThat(list.length, is(0));
vm.put(100);
list = vm.getBuyableJuices();
assertThat(list.length, is(1));
assertThat(list[0].getName(), is("水"));
vm.put(100);
list = vm.getBuyableJuices();
assertThat(list.length, is(3));
}
手抜きの実装でテストをパスさせる。
public Juice[] getBuyableJuices() {
if (total >= 200) {
Juice[] list = {
new Juice("水", 100),
new Juice("コーラ", 120),
new Juice("レッドブル", 200)
};
return list;
}
if (total >= 120) {
Juice[] list = {
new Juice("水", 100),
new Juice("コーラ", 120),
};
return list;
}
if (total >= 100) {
Juice[] list = {
new Juice("水", 100),
};
return list;
}
return new Juice[0];
}
在庫がなくなったらリストに出ないテストを作成する。
合計700円を投入して水を5個買うと、200円残る。
水の在庫がなくなったので、リストはコーラとレッドブルの2個だけになる。
@Test
void 購入可能なドリンクのリスト() {
VendingMachine vm = new VendingMachine();
Juice[] list = vm.getBuyableJuices();
assertThat(list.length, is(0));
vm.put(100);
list = vm.getBuyableJuices();
assertThat(list.length, is(1));
assertThat(list[0].getName(), is("水"));
vm.put(100);
list = vm.getBuyableJuices();
assertThat(list.length, is(3));
vm.put(500);
for (int i = 0; i < 5; i++) {
Juice j = vm.buy("水");
assertThat(j.getName(), is("水"));
}
list = vm.getBuyableJuices();
assertThat(list.length, is(2));
}
public Juice[] getBuyableJuices() {
Collection<List<Juice>> c = map.values();
List<Juice> list = new ArrayList<>();
for (List<Juice> l : c) {
if (l.size() == 0) continue;
Juice j = l.get(0);
if (j.getPrice() > total) continue;
list.add(j);
}
return list.toArray(new Juice[0]);
}