5月8日

TDDでFizzBuzzを実装する続き

前回作成したFizzBuzzクラスのコード

package tdd;

public class FizzBuzz {
	public String say(int i) {
		return String.valueOf(i);
	}
}

前回作成したテストコード

package tdd;

import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;

import org.junit.Test;

public class FizzBuzzTest {

	@Test
	public void testSay1() {
		FizzBuzz fz = new FizzBuzz();
		String s = fz.say(1);
		assertThat(s, is("1"));
	}

	@Test
	public void testSay2() {
		FizzBuzz fz = new FizzBuzz();
		String s = fz.say(2);
		assertThat(s, is("2"));
	}

	@Test
	public void testSay3() {
		FizzBuzz fz = new FizzBuzz();
		String s = fz.say(3);
		assertThat(s, is("Fizz"));
	}

}

テストを実行するとエラーになるので、テストにパスするようにFizzBuzzクラスを修正する。

package tdd;

public class FizzBuzz {
	public String say(int i) {
		if (i == 3) return "Fizz";
		return String.valueOf(i);
	}
}

テストにパスする。
4と5に対するテストコードを追加する。
通常は4の場合は数値を文字列化するだけなので、テストを書かないが、ここでは練習のためにテストを作成する。
5の場合はBuzzと返すので、テストにパスしなくなる。

	@Test
	public void testSay5() {
		FizzBuzz fz = new FizzBuzz();
		String s = fz.say(5);
		assertThat(s, is("Buzz"));
	}

FizzBuzzクラスを修正する。

package tdd;

public class FizzBuzz {
	public String say(int i) {
		if (i == 3) return "Fizz";
		if (i == 5) return "Buzz";
		return String.valueOf(i);
	}
}

テストにパスする。
次に6の場合のテストを追加する。

	@Test
	public void testSay6() {
		FizzBuzz fz = new FizzBuzz();
		String s = fz.say(6);
		assertThat(s, is("Fizz"));
	}

FizzBuzzクラスを修正する。

package tdd;

public class FizzBuzz {
	public String say(int i) {
		if (i % 3 == 0) return "Fizz";
		if (i == 5) return "Buzz";
		return String.valueOf(i);
	}
}

同時に5のif文も書き換えたいけど残しておく、先にテストを書く。
7~9はうまくいきそうなので、テストを書かないことにする。
10ではエラーとなるはずなので、テストを書く。

	@Test
	public void testSay10() {
		FizzBuzz fz = new FizzBuzz();
		String s = fz.say(10);
		assertThat(s, is("Buzz"));
	}

テストにパスするようにFizzBuzzクラスを修正する。

FizzBuzzクラスを修正する。

package tdd;

public class FizzBuzz {
	public String say(int i) {
		if (i % 3 == 0) return "Fizz";
		if (i % 5 == 0) return "Buzz";
		return String.valueOf(i);
	}
}

11~14はうまくいきそうなのでテストは書かないでおく。
15ではFizzBuzzと返されるというテストを追加する。

	@Test
	public void testSay15() {
		FizzBuzz fz = new FizzBuzz();
		String s = fz.say(15);
		assertThat(s, is("FizzBuzz"));
	}

さらにTDDの練習問題を行う
TDDBC大阪の課題を使わせてもらう。
TDD Boot Camp 大阪 3.0/課題( #tddbc )

自動販売機クラス(VendingMachine)を作成する。
最初の課題は、お金を投入することなので、putMoney()メソッドを作成する。
投入した金額の総額を返すということなので、戻り値でその値を返すようにする。

package tdd;

public class VendingMachine {
	public int putMoney(int amount) {
		return 0;
	}
}

テストを作成する。
新規でJUnitテストケースを作成する。
テスト対象メソッドとしてputMoney()にチェックを入れておく。

package tdd;

import static org.junit.Assert.*;

import org.junit.Test;

public class VendingMachineTest {

	@Test
	public void testPutMoney() {
		fail("まだ実装されていません");
	}
}

10円を投入するテストを作成する。

	@Test
	public void testPutMoney() {
		VendingMachine vm = new VendingMachine();
		int v = vm.putMoney(10);
		assertThat(v, is(10));
	}

テストにパスするように修正する。

	public int putMoney(int amount) {
		return 10;
	}

さらに10円を投入するテストを追加する。

	@Test
	public void testPutMoney() {
		VendingMachine vm = new VendingMachine();
		int v = vm.putMoney(10);
		assertThat(v, is(10));
		v = vm.putMoney(10);
		assertThat(v, is(20));
	}
}

テストにパスするように修正する。

	public int putMoney(int amount) {
		total += amount;
		return total;
	}

払い戻し操作を追加する。
メソッド名をrefund()とする。
まずテストを追加する。

	@Test
	public void testPutMoney() {
		VendingMachine vm = new VendingMachine();
		int v = vm.putMoney(10);
		assertThat(v, is(10));
		v = vm.putMoney(10);
		assertThat(v, is(20));
		v = vm.refund();
		assertThat(v, is(20));
	}

}

テストにパスするように修正する。

package tdd;

public class VendingMachine {
	private int total;
	public int putMoney(int amount) {
		total += amount;
		return total;
	}
	public int refund() {
		return total;
	}
}

お金を再投入して払い戻すテストを追加してみる。

	@Test
	public void testPutMoney() {
		VendingMachine vm = new VendingMachine();
		int v = vm.putMoney(10);
		assertThat(v, is(10));
		v = vm.putMoney(10);
		assertThat(v, is(20));
		v = vm.refund();
		assertThat(v, is(20));
		vm.putMoney(100);
		v = vm.refund();
		assertThat(v, is(100));
	}

}

テストにパスするように修正する。

package tdd;

public class VendingMachine {
	private int total;
	public int putMoney(int amount) {
		total += amount;
		return total;
	}
	public int refund() {
		int r = total;
		total = 0;
		return r;
	}
}

1円や5円などが投入されたらそのまま釣銭として出力する。
どう表現するか?
putMoney()メソッドの戻り値で釣銭を出力するように仕様変更して、合計金額を取得するメソッドを追加することにする。

テストを修正する。

	@Test
	public void testPutMoney() {
		VendingMachine vm = new VendingMachine();
		int v = vm.putMoney(10);
		assertThat(v, is(0));
		v = vm.getTotal();
		assertThat(v, is(10));

		v = vm.putMoney(10);
		assertThat(v, is(0));
		v = vm.getTotal();
		assertThat(v, is(20));

		v = vm.putMoney(50);
		assertThat(v, is(0));
		v = vm.getTotal();
		assertThat(v, is(70));

		v = vm.refund();
		assertThat(v, is(70));

		vm.putMoney(100);
		v = vm.refund();
		assertThat(v, is(100));
	}

テストにパスするように修正する。

package tdd;

public class VendingMachine {
	private int total;
	public int putMoney(int amount) {
		total += amount;
		return 0;
	}
	public int refund() {
		int r = total;
		total = 0;
		return r;
	}
	public int getTotal() {
		return total;
	}
}

1円や5円など、受け付けない金額が投入された場合のテストを書く。

	@Test
	public void testPutMoneyNG() {
		VendingMachine vm = new VendingMachine();
		int v = vm.putMoney(1);
		assertThat(v, is(1));
		v = vm.getTotal();
		assertThat(v, is(0));
	}

テストにパスするように修正する。

	public int putMoney(int amount) {
		if (amount == 10 ||
			amount == 50 ||
			amount == 100 ||
			amount == 500 ||
			amount == 1000) {
			total += amount;
			return 0;
		}
		return amount;
	}

値段と名前の属性からなるジュースを1種類格納できるということなので、まずはJuiceクラスを作成する。

package tdd;

public class Juice {
	private String name;
	private int 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;
	}
}

自動販売機に格納されているジュースの名前と金額を取得できるようにする。
実際の自動販売機でサンプルが並んでいる様子を表現しようとしている。
Juiceクラスを作成したので、Juiceクラスの配列を取得するという仕様にしてみる。

	@Test
	public void testJuiceVariety() {
		VendingMachine vm = new VendingMachine();
		Juice[] j = vm.getJuiceVariety();
		assertThat(j.length, is(1));
		assertThat(j[0].getName(), is("コーラ"));
		assertThat(j[0].getPrice(), is(120));
	}

テストにパスするように修正する。

	public Juice[] getJuiceVariety() {
		Juice[] j = new Juice[1];
		Juice cola = new Juice();
		cola.setName("コーラ");
		cola.setPrice(120);
		j[0] = cola;
		return j;
	}

在庫を取得できるようにするために、テストを追加する。

	@Test
	public void testJuiceStock() {
		VendingMachine vm = new VendingMachine();
		int v = vm.getStock("コーラ");
		assertThat(v, is(5));
	}

getStock()メソッドを生成してコンパイルエラーを解消する。
テストはパスしない状態で終了。
次回へ続く。

	public int getStock(String string) {
		return 0;
	}