6月15日

Mapを使用したクラス

ゲームのタイトルをキーにして会社名を値として保存するクラスの続き。
ひとつのデータを格納して会社名を取得するテストを追加する。

GamesTest.java

	@Test
	public void testGet() {
		Games g = new Games();
		g.add("スーパーマリオブラザーズ", "任天堂");
		String s = g.get("スーパーマリオブラザーズ");
		assertThat(s, is("任天堂"));
	}

このテストにパスするには、Fakeすればよい。

Games.java

import java.util.HashMap;
import java.util.Map;


public class Games {
	private Map<String, String> map = new HashMap<String, String>();

	public void add(String title, String company) {
		map.put(title, company);
	}

	public int getCount() {
		return map.size();
	}

	public String get(String string) {
		return "任天堂";
	}
}

さらにテストを追加する。

	@Test
	public void testGet1() {
		Games g = new Games();
		g.add("パズドラ", "ガンホー");
		String s = g.get("パズドラ");
		assertThat(s, is("ガンホー"));
	}

両方のテストをパスするためには、Mapを使用してきちんと実装するのがよい。

Games.java

	public String get(String key) {
		return map.get(key);
	}

ファイルから読み込む

map.txt を作成して、タイトルと会社名のデータを書き込んでおく。

map.txt

スーパーマリオブラザーズ,任天堂
パズドラ,ガンホー
妖怪ウォッチ,レベルファイブ
ドラゴンクエスト,スクエアエニックス

このファイルのデータを読み込むテストを作成する。

GamesTest.java

	@Test
	public void testLoad() {
		Games g = new Games();
		g.load("map.txt");
		int n = g.getCount();
		assertThat(n, is(4));
	}

Gamesクラスにload()メソッドを追加して、指定されたファイルの内容を読み込み、マップに追加する。
ファイルを読み込む部分は、TextDocumentクラスのload()メソッドが参考になる。

Games.java

	public boolean load(String path) {
		File f = new File(path);
		if (!f.exists()) return false;
		try (BufferedReader in = new BufferedReader(new FileReader(path))) {
			while (in.ready()) {
				String s = in.readLine();
				String[] sa = s.split(",");
				map.put(sa[0], sa[1]);
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
		return true;
	}

テキストファイルに出力する機能を追加する

格納している内容をテキストファイルに出力し、出力したファイルを読み込んで行数が一致するかを確認するテストを作成する。

GamesTest.java

	@Test
	public void testSave() {
		Games g = new Games();
		g.add("パズドラ", "ガンホー");
		g.save("map1.txt");
		boolean result = g.load("map1.txt");
		assertThat(result, is(true));
		int n = g.getCount();
		assertThat(n, is(1));
	}

保存の処理内容は、TextDocumentクラスのsave()とほぼ同じ。
タイトルと会社名をカンマで区切って出力すればよい。
ループする部分はテキストp.444が参考になる。

Games.java

	public void save(String path) {
		try (PrintWriter out = new PrintWriter(new FileWriter(path))) {
			Set<String> keys = map.keySet();
			for (String key : keys) {
				String v = map.get(key);
				out.println(key + "," + v);
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

要素の削除

指定したタイトルを削除する機能を追加する。
まずテストを作成する。

GamesTest.java

	@Test
	public void testDelete() {
		Games g = new Games();
		g.load("map.txt");
		int n = g.getCount();
		assertThat(n, is(4));
		g.delete("パズドラ");
		n = g.getCount();
		assertThat(n, is(3));
		String s = g.get("パズドラ");
		assertNull(s);
	}

テストにパスするようにGames#delete()を実装する。
要素の削除はテキストには掲載されてないが、Eclipseの補完機能で表示される候補から探せば見つかる。

6月12日

ストリームとコレクション

前回、TextDocumentクラスをテスト駆動開発で作成し始めたので、その続きを実施する。

  1. 指定した行の文字列取得
  2. 指定した行を削除(削除した行の文字列を返す)
  3. 指定した文字列を行として末尾に挿入
  4. 指定した場所に指定した文字列を行として挿入
  5. 出力先ファイル名を指定してファイル出力

指定した場所に指定した文字列を行として挿入

TextDocument.java

	public void addLine(int i, String s) {
		lines.add(i, s);
	}

前回は、4まで実施したが、先頭行に挿入するテストしかやっていないので、別のテストを追加して動作確認を実施する。

まずは、途中に挿入するテストを追加してみる。

	@Test
	public void testAdd2() {
		TextDocument td = new TextDocument();
		td.load("hoge.txt");
		int lines = td.getLines();
		assertThat(lines, is(10));
		td.addLine(3, "1234567890");
		lines = td.getLines();
		assertThat(lines, is(11));
		String s = td.getLine(0);
		assertThat(s, is("ab"));
		s = td.getLine(3);
		assertThat(s, is("1234567890"));
		s = td.getLine(4);
		assertThat(s, is("abcde"));
	}

範囲外の -1 行目を引数で指定した場合は例外が発生する。
例外の発生をテストで書くには、Testアノテーションの引数に発生する例外を記述する。

TextDocumentTest.java

	@Test(expected = IndexOutOfBoundsException.class)
	public void testAdd3() {
		TextDocument td = new TextDocument();
		td.load("hoge.txt");
		td.addLine(-1, "1234567890");
	}

10行しかないときに11行目に追加しようとしても例外が発生する。
まずは、例外が発生してエラーになるテストを書く。
TextDocumentTest.java

	@Test
	public void testAdd4() {
		TextDocument td = new TextDocument();
		td.load("hoge.txt");
		td.addLine(11, "1234567890");
	}

例外が発生してエラーになることを確認してから、アノテーションに expected 引数を追加する。

	@Test(expected = IndexOutOfBoundsException.class)
	public void testAdd4() {
		TextDocument td = new TextDocument();
		td.load("hoge.txt");
		td.addLine(11, "1234567890");
	}

次はファイルに書き込む機能を追加する。
まずはテストを書く。
ファイルに書けたかどうかは、ファイルを読み込んでテストしてみる。
TextDocumentTest.java

	@Test
	public void testSave() {
		TextDocument td = new TextDocument();
		td.load("hoge.txt");
		td.addLine(0, "1234567890");
		td.save("out.txt");
		td.load("out.txt");
		int lines = td.getLines();
		assertThat(lines, is(11));
	}

TextDocumentに、保存の処理を実装する。

	public void save(String path) {
		try (PrintWriter out = new PrintWriter(new FileWriter(path))) {
			for (String s : lines) {
				out.println(s);
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

Mapの使用例

ゲームタイトルをキーにして会社名を保存するクラスを作成する。

Games.java

public class Games {
}

テストを作る。
GamesTest.java

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

import org.junit.Test;


public class GamesTest {

	@Test
	public void testAdd() {
		Games g = new Games();
		g.add("スーパーマリオブラザーズ", "任天堂");
		int n = g.getCount();
		assertThat(n, is(1));
		g.add("パズドラ", "ガンホー");
		n = g.getCount();
		assertThat(n, is(2));
	}

}

テストにパスするように Games を実装したら、さらにテストを追加する。
GamesTest.java

	@Test
	public void testGet() {
		Games g = new Games();
		g.add("スーパーマリオブラザーズ", "任天堂");
		String s = g.get("スーパーマリオブラザーズ");
		assertThat(s, is("任天堂"));
	}

6月8日

テスト駆動開発でストリームとコレクションを復習する

TextDocumentクラスと、テストケースのTextDocumentTestクラスを作成する。

ファイルを読み込む機能を用意する。
そのために、load()メソッドを作成する。
まずはテストを書く。

TextDocumentTest.java

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

import org.junit.Test;

public class TextDocumentTest {

	@Test
	public void testLoad() {
		TextDocument td = new TextDocument();
		boolean b = td.load("hoge.txt");
		assertThat(b, is(true));
	}

}

コンパイルエラーを解消するために、TextDocumentクラスにload()メソッドを追加する。
TextDocument.java

public class TextDocument {
	public boolean load(String path) {
		return false;
	}
}

テストを実行するとエラーになるので、load()メソッドの戻り値をtrueに変更する。(Fake)
TextDocument.java

public class TextDocument {
	public boolean load(String path) {
		return true;
	}
}

テストにパスするのでめでたしめでたし。
テストケースに、パスしないテストを追加する。
TextDocumentTest.java

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

import org.junit.Test;

public class TextDocumentTest {

	@Test
	public void testLoadNG() {
		TextDocument td = new TextDocument();
		boolean b = td.load("aaa.txt");
		assertThat(b, is(false));
	}

	@Test
	public void testLoad() {
		TextDocument td = new TextDocument();
		boolean b = td.load("hoge.txt");
		assertThat(b, is(true));
	}

}

このテストをパスするようにするには、まじめに実装しないといけない。
TextDocument.java

import java.io.File;

public class TextDocument {
	public boolean load(String path) {
		File f = new File(path);
		if (!f.exists()) return false;
		return true;
	}
}

次に、ファイルの行数を取得するメソッドを作ることにする。
hoge.txt は11行なので、テストコードは以下のようになる。
TextDocumentTest.java

	@Test
	public void testLines() {
		TextDocument td = new TextDocument();
		boolean b = td.load("hoge.txt");
		assertThat(b, is(true));
		int lines = td.getLines();
		assertThat(lines, is(11));
	}

このテストをパスするには、Fakeすればよい。
TextDocument.java

import java.io.File;

public class TextDocument {
	public boolean load(String path) {
		File f = new File(path);
		if (!f.exists()) return false;
		return true;
	}

	public int getLines() {
		return 11;
	}
}

Fakeコードではテストにパスしないようにするために、1行だけのファイル 1.txt を用意し、そのファイルの行数を検査するテストを追加する。
TextDocumentTest.java

	@Test
	public void testLines() {
		TextDocument td = new TextDocument();
		boolean b = td.load("hoge.txt");
		assertThat(b, is(true));
		int lines = td.getLines();
		assertThat(lines, is(11));
	}

	@Test
	public void testLines1() {
		TextDocument td = new TextDocument();
		boolean b = td.load("1.txt");
		assertThat(b, is(true));
		int lines = td.getLines();
		assertThat(lines, is(1));
	}

このテストをパスするために、TextDocumentクラスをきちんと実装する。
java.util.List を使用してコレクションフレームワークの復習をする。
List の要素として、ファイルの各行を追加する。

TextDocument.java

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

public class TextDocument {
	private List<String> lines;

	public boolean load(String path) {
		lines = new ArrayList<String>();
		File f = new File(path);
		if (!f.exists()) return false;
		try (BufferedReader in = new BufferedReader(new FileReader(path))) {
			while (in.ready()) {
				String s = in.readLine();
				lines.add(s);
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
		return true;
	}

	public int getLines() {
		return lines.size();
	}
}

ファイル読み込みができた人は、以下の機能を追加しましょう!

  1. 指定した行の文字列取得
  2. 指定した行を削除(削除した行の文字列を返す)
  3. 指定した文字列を行として末尾に挿入
  4. 指定した場所に指定した文字列を行として挿入
  5. 出力先ファイル名を指定してファイル出力

上記機能追加ができたら、さらに以下の機能を追加しましょう!

  1. 文字列検索(行番号と、その行の文字列を、Listで返す)
  2. 文字列置換(検索と同じように返す)

指定した行の文字列を取得

まずはテストを書く。

TextDocumentTest.java

	@Test
	public void testGetLine() {
		TextDocument td = new TextDocument();
		boolean b = td.load("hoge.txt");
		String s = td.getLine(0);
		assertThat(s, is("ab"));
	}

TextDocumentにはgetLine()メソッドがないので、Eclipseに作ってもらう。
TextDocument.java

	public String getLine(int i) {
		return null;
	}

テストを実行するとエラーになるので、テストをパスするように “ab”を返すようにFakeする。
TextDocument.java

	public String getLine(int i) {
		return "ab";
	}

テストを追加する。
TextDocumentTest.java

	@Test
	public void testGetLine() {
		TextDocument td = new TextDocument();
		boolean b = td.load("hoge.txt");
		String s = td.getLine(0);
		assertThat(s, is("ab"));
		s = td.getLine(1);
		assertThat(s, is("abc"));
	}

テストを実行するとエラーになるので、テストにパスするようにコードを修正する。
TextDocument.java

	public String getLine(int i) {
		return lines.get(i);
	}

指定した行を削除

まずはテストを書く。
削除した行の文字列を返す。

TextDocumentTest.java

	@Test
	public void testDelete() {
		TextDocument td = new TextDocument();
		td.load("hoge.txt");
		String s = td.deleteLine(0);
		assertThat(s, is("ab"));
	}

EclipseにdeleteLine()メソッドを作ってもらう。
TextDocument.java

	public String deleteLine(int i) {
		return null;
	}

テストを実行してもパスしないので、”ab”を返すようにFakeする。
TextDocument.java

	public String deleteLine(int i) {
		return "ab";
	}

テストを追加する。
1行目を削除したら、もともと2行目だったものが1行目になるはず。
TextDocumentTest.java

	@Test
	public void testDelete() {
		TextDocument td = new TextDocument();
		td.load("hoge.txt");
		String s = td.deleteLine(0);
		assertThat(s, is("ab"));
		s = td.getLine(0);
		assertThat(s, is("abc"));
	}

TextDocumentのdeleteLine()を修正。

TextDocument.java

	public String deleteLine(int i) {
		return lines.remove(i);
	}

指定した文字列を行として末尾に挿入

まずはテストを書く。
1行追加すれば、行数が増えるはず。

TextDocumentTest.java

	@Test
	public void testAdd() {
		TextDocument td = new TextDocument();
		td.load("hoge.txt");
		int lines = td.getLines();
		assertThat(lines, is(10));
		td.addLine("1234567890");
		lines = td.getLines();
		assertThat(lines, is(11));
	}

EclipseにaddLine()を作ってもらう。
テストするとエラーになるので、テストにパスする実装を追加する。
TextDocument.java

	public void addLine(String s) {
		lines.add(s);
	}

指定した場所に指定した文字列を行として挿入

まずはテストを書く。
どこに追加するのかを引数で渡す。
先頭に1行追加すれば、行数が増えるはず。
先頭行は追加した文字列に一致するはず。
2行目は、もともと1行目だった文字列に一致するはず。

TextDocumentTest.java

	@Test
	public void testAdd1() {
		TextDocument td = new TextDocument();
		td.load("hoge.txt");
		int lines = td.getLines();
		assertThat(lines, is(10));
		td.addLine(0, "1234567890");
		lines = td.getLines();
		assertThat(lines, is(11));
		String s = td.getLine(0);
		assertThat(s, is("1234567890"));
		s = td.getLine(1);
		assertThat(s, is("ab"));
	}

6月1日

テスト駆動開発で自動販売機を作る

前回は10円玉の釣り銭を使い果たすテストを用意したところまで。
テストを実行すると失敗するので、テストにパスするよう実装する。

VendingMachineTest.java

 
	@Test
	public void testChangeStock6() {
		VendingMachine vm = new VendingMachine();
		for (int i = 0; i < 3; i++) {
			vm.throwInto(100);
			vm.throwInto(50);
			vm.purchace();
		}
		int s10 = vm.get10YenStock();
		assertThat(s10, is(1));
		vm.throwInto(100);
		vm.throwInto(50);
		Output o = vm.purchace();
		s10 = vm.get10YenStock();
		assertThat(s10, is(1));
		assertThat(o.getJuiceCount(), is(0));
		assertThat(o.getChange(), is(0));
		int total = vm.getTotal();
		assertThat(total, is(150));
	}

お釣りが足りないときは購入できないようにすればいい。ということは、購入可能かどうかを調べているisPurchacable()メソッド内でお釣りが足りるかどうかを確認すればいい。

そこで、お釣りが足りるかどうかを調べる isChangeShorted()メソッドを追加する。お釣りが足りるならfalse、足りないならtrueを返す。

常にfalseを返すように作れば、従来どおりの動作となる。

	public boolean isPurchacable() {
		if (getJuiceStock() == 0) return false;
		if (total < getJuicePrice()) return false;
		if (isChangeShorted()) return false;
		return true;
	}

	private boolean isChangeShorted() {
		return false;
	}

お釣りが50円よりも少ないときは10円玉のお釣りを出す必要がある。
10円玉のストックとお釣りの金額を比較して、釣り銭が足りるかどうかを調べる。

	private boolean isChangeShorted() {
		int change = total - getJuicePrice();
		if (change < 50) {
			if (stock10yen * 10 < change) return true;
		}
		return false;
	}

100円玉の釣り銭が足りなくなるテストを作る。
VendingMachineTest.java

	@Test
	public void testChangeStock7() {
		VendingMachine vm = new VendingMachine();
		for (int i = 0; i < 2; i++) {
			vm.throwInto(500);
			vm.throwInto(10);
			vm.throwInto(10);
			vm.purchace();
		}
		int s100 = vm.get100YenStock();
		assertThat(s100, is(2));
		vm.throwInto(500);
		Output o = vm.purchace();
		s100 = vm.get100YenStock();
		assertThat(s100, is(2));
		assertThat(o.getJuiceCount(), is(0));
		assertThat(o.getChange(), is(0));
	}

10円玉の釣り銭不足と同様の実装を追加する。

VendingMachine.java

	private boolean isChangeShorted() {
		int change = total - getJuicePrice();
		if (change < 50) {
			if (stock10yen * 10 < change) return true;
		}
		if (change < 500) {
			if (stock100yen * 100 < change) return true;
		}
		return false;
	}

これを追加すると、testPurchace1000() が通らなくなってしまった!
テストコードを確認してみると、お釣りのチェックをしなければパスするコードを書いていることがわかる。
このテストでは、ジュースの在庫がなくなるのを確認したいので、お釣りが不要になるようにテストコードを修正する。

VendingMachineTest.java

	@Test
	public void testPurchace1000() {
		VendingMachine vm = new VendingMachine();
		vm.throwInto(1000);
		Output o = vm.purchace();
		assertThat(o.getJuiceCount(), is(1));
		assertThat(o.getChange(), is(880));
		int stock = vm.getJuiceStock();
		assertThat(stock, is(4));
		for (int i = 0; i < 4; i++) {
			//vm.throwInto(500); 500円ではなく、釣り銭が出ない120円で購入
			vm.throwInto(100);
			vm.throwInto(10);
			vm.throwInto(10);
			o = vm.purchace();
		}
		stock = vm.getJuiceStock();
		assertThat(stock, is(0));
		vm.throwInto(500);
		o = vm.purchace();
		assertThat(o.getJuiceCount(), is(0));
		assertThat(o.getChange(), is(0));
		int total = vm.getTotal();
		assertThat(total, is(500));
	}

50円玉の釣り銭不足になるテストを作りたいが、ジュースの在庫が5個しかないのでできない。
500円玉の釣り銭不足も同様。
ここまでで、テスト駆動開発の実習を終わりにする。

VendingMachineTest.java

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

import org.junit.Test;


public class VendingMachineTest {

	@Test
	public void testThrowInto() {
		VendingMachine vm = new VendingMachine();
		int change = vm.throwInto(10);
		assertThat(change, is(0));
		int total = vm.getTotal();
		assertThat(total, is(10));
		change = vm.throwInto(50);
		assertThat(change, is(0));
		total = vm.getTotal();
		assertThat(total, is(60));
	}

	@Test
	public void testThrowIntoNotAccept() {
		VendingMachine vm = new VendingMachine();
		int change = vm.throwInto(1);
		assertThat(change, is(1));
		int total = vm.getTotal();
		assertThat(total, is(0));
		change = vm.throwInto(5);
		assertThat(change, is(5));
		change = vm.throwInto(2000);
		assertThat(change, is(2000));
		change = vm.throwInto(5000);
		assertThat(change, is(5000));
		change = vm.throwInto(10000);
		assertThat(change, is(10000));
	}

	@Test
	public void testThrowIntoRefund() {
		VendingMachine vm = new VendingMachine();
		int change = vm.throwInto(10);
		assertThat(change, is(0));
		int total = vm.getTotal();
		assertThat(total, is(10));
		int refund = vm.refund();
		assertThat(refund, is(10));
		total = vm.getTotal();
		assertThat(total, is(0));
		change = vm.throwInto(100);
		assertThat(change, is(0));
		total = vm.getTotal();
		assertThat(total, is(100));
		change = vm.throwInto(500);
		assertThat(change, is(0));
		total = vm.getTotal();
		assertThat(total, is(600));
		refund = vm.refund();
		assertThat(refund, is(600));
	}

	@Test
	public void testGetJuice() {
		VendingMachine vm = new VendingMachine();
		String name = vm.getJuiceName();
		assertThat(name, is("コーラ"));
		int price = vm.getJuicePrice();
		assertThat(price, is(120));
		int stock = vm.getJuiceStock();
		assertThat(stock, is(5));
	}

	@Test
	public void testIsPurchacable() {
		VendingMachine vm = new VendingMachine();
		boolean b = vm.isPurchacable();
		assertThat(b, is(false));
		vm.throwInto(500);
		b = vm.isPurchacable();
		assertThat(b, is(true));
	}

	@Test
	public void testPurchace() {
		VendingMachine vm = new VendingMachine();
		vm.throwInto(100);
		vm.throwInto(10);
		vm.throwInto(10);
		Output o = vm.purchace();
		assertThat(o.getJuiceCount(), is(1));
		assertThat(o.getChange(), is(0));
		int total = vm.getTotal();
		assertThat(total, is(0));
	}

	@Test
	public void testPurchace500() {
		VendingMachine vm = new VendingMachine();
		vm.throwInto(500);
		Output o = vm.purchace();
		assertThat(o.getJuiceCount(), is(1));
		assertThat(o.getChange(), is(380));
		int total = vm.getTotal();
		assertThat(total, is(0));
	}

	@Test
	public void testPurchace1000() {
		VendingMachine vm = new VendingMachine();
		vm.throwInto(1000);
		Output o = vm.purchace();
		assertThat(o.getJuiceCount(), is(1));
		assertThat(o.getChange(), is(880));
		int stock = vm.getJuiceStock();
		assertThat(stock, is(4));
		for (int i = 0; i < 4; i++) {
			vm.throwInto(100);
			vm.throwInto(10);
			vm.throwInto(10);
			o = vm.purchace();
		}
		stock = vm.getJuiceStock();
		assertThat(stock, is(0));
		vm.throwInto(500);
		o = vm.purchace();
		assertThat(o.getJuiceCount(), is(0));
		assertThat(o.getChange(), is(0));
		int total = vm.getTotal();
		assertThat(total, is(500));
	}

	@Test
	public void testSale() {
		VendingMachine vm = new VendingMachine();
		int sale = vm.getSale();
		assertThat(sale, is(0));
		vm.throwInto(500);
		vm.purchace();
		sale = vm.getSale();
		assertThat(sale, is(120));
	}

	@Test
	public void testChangeStock() {
		VendingMachine vm = new VendingMachine();
		int s10 = vm.get10YenStock();
		assertThat(s10, is(10));
		int s50 = vm.get50YenStock();
		assertThat(s50, is(10));
		int s100 = vm.get100YenStock();
		assertThat(s100, is(10));
		int s500 = vm.get500YenStock();
		assertThat(s500, is(10));
		vm.throwInto(100);
		vm.throwInto(50);
		Output o = vm.purchace();
		assertThat(o.getJuiceCount(), is(1));
		assertThat(o.getChange(), is(30));
		s10 = vm.get10YenStock();
		assertThat(s10, is(7));
	}

	@Test
	public void testChangeStock2() {
		VendingMachine vm = new VendingMachine();
		vm.throwInto(100);
		vm.throwInto(50);
		Output o = vm.purchace();
		assertThat(o.getJuiceCount(), is(1));
		assertThat(o.getChange(), is(30));
		int s10 = vm.get10YenStock();
		assertThat(s10, is(7));

		vm.throwInto(100);
		vm.throwInto(50);
		vm.purchace();
		s10 = vm.get10YenStock();
		assertThat(s10, is(4));
	}

	@Test
	public void testChangeStock3() {
		VendingMachine vm = new VendingMachine();
		vm.throwInto(100);
		vm.throwInto(100);
		vm.purchace();
		int s10 = vm.get10YenStock();
		assertThat(s10, is(7));
		int s50 = vm.get50YenStock();
		assertThat(s50, is(9));
	}

	@Test
	public void testChangeStock4() {
		VendingMachine vm = new VendingMachine();
		vm.throwInto(500);
		vm.purchace();
		int s10 = vm.get10YenStock();
		assertThat(s10, is(7));
		int s50 = vm.get50YenStock();
		assertThat(s50, is(9));
		int s100 = vm.get100YenStock();
		assertThat(s100, is(7));
	}

	@Test
	public void testChangeStock5() {
		VendingMachine vm = new VendingMachine();
		vm.throwInto(1000);
		vm.purchace();
		int s10 = vm.get10YenStock();
		assertThat(s10, is(7));
		int s50 = vm.get50YenStock();
		assertThat(s50, is(9));
		int s100 = vm.get100YenStock();
		assertThat(s100, is(7));
		int s500 = vm.get500YenStock();
		assertThat(s500, is(9));
	}

	@Test
	public void testChangeStock6() {
		VendingMachine vm = new VendingMachine();
		for (int i = 0; i < 3; i++) {
			vm.throwInto(100);
			vm.throwInto(50);
			vm.purchace();
		}
		int s10 = vm.get10YenStock();
		assertThat(s10, is(1));
		vm.throwInto(100);
		vm.throwInto(50);
		Output o = vm.purchace();
		s10 = vm.get10YenStock();
		assertThat(s10, is(1));
		assertThat(o.getJuiceCount(), is(0));
		assertThat(o.getChange(), is(0));
		int total = vm.getTotal();
		assertThat(total, is(150));
	}

	@Test
	public void testChangeStock7() {
		VendingMachine vm = new VendingMachine();
		for (int i = 0; i < 2; i++) {
			vm.throwInto(500);
			vm.throwInto(10);
			vm.throwInto(10);
			vm.purchace();
		}
		int s100 = vm.get100YenStock();
		assertThat(s100, is(2));
		vm.throwInto(500);
		Output o = vm.purchace();
		s100 = vm.get100YenStock();
		assertThat(s100, is(2));
		assertThat(o.getJuiceCount(), is(0));
		assertThat(o.getChange(), is(0));
	}
}

VendingMachine.java


public class VendingMachine {
	private int total = 0;
	private int stock = 5;
	private int sale = 0;
	private int stock10yen = 10;
	private int stock50yen = 10;
	private int stock100yen = 10;
	private int stock500yen = 10;

	public int throwInto(int yen) {
		if (yen == 1) return 1;
		if (yen == 5) return 5;
		if (yen == 2000) return 2000;
		if (yen == 5000) return 5000;
		if (yen == 10000) return 10000;
		total += yen;
		return 0;
	}

	public int getTotal() {
		return total;
	}

	public int refund() {
		int refund = total;
		total = 0;
		return refund;
	}

	public String getJuiceName() {
		return "コーラ";
	}

	public int getJuicePrice() {
		return 120;
	}

	public int getJuiceStock() {
		return stock;
	}

	public boolean isPurchacable() {
		if (getJuiceStock() == 0) return false;
		if (total < getJuicePrice()) return false;
		if (isChangeShorted()) return false;
		return true;
	}

	private boolean isChangeShorted() {
		int change = total - getJuicePrice();
		if (change < 50) {
			if (stock10yen * 10 < change) return true;
		}
		if (change < 500) {
			if (stock100yen * 100 < change) return true;
		}
		return false;
	}

	public Output purchace() {
		if (!isPurchacable()) {
			return new Output(0, 0);
		}
		total -= getJuicePrice();
		int change = total;
		decreaseStock(change);
		total -= change;
		stock--;
		sale += getJuicePrice();
		return new Output(1, change);
	}

	private void decreaseStock(int change) {
		if (change >= 500) {
			change -= 500;
			stock500yen--;
		}
		if (change >= 100) {
			int c = change / 100;
			change -= c * 100;
			stock100yen -= c;
		}
		if (change >= 50) {
			change -= 50;
			stock50yen--;
		}
		stock10yen -= change / 10;
	}

	public int getSale() {
		return sale;
	}

	public int get10YenStock() {
		return stock10yen;
	}

	public int get50YenStock() {
		return stock50yen;
	}

	public int get100YenStock() {
		return stock100yen;
	}

	public int get500YenStock() {
		return stock500yen;
	}
}

スレッド

スレッドは、並行処理を行う仕組み。
Javaでは言語仕様で並行処理をサポートしている。

Threadクラスを継承してクラスを作成する。そのクラスのインスタンスを生成し、start() メソッドを呼ぶとスレッドが起動される。スレッドが起動されると、run() メソッドが呼び出される。

Threadを使ったサンプル

MyThread.java

public class MyThread extends Thread {
	@Override
	public void run() {
		for (int i = 0; i < 5; i++) {
			System.out.println("Threadの実行中" + i);
		}
	}
}

MyThreadTest.java

public class MyThreadTest {

	public static void main(String[] args) {
		Thread t = new MyThread();
		t.start();
		for (int i = 0; i < 5; i++) {
			System.out.println("mainの実行中" + i);
		}
	}

}

Runnableインタフェースを使用する方法もある。

MyRunnable.java

public class MyRunnable implements Runnable {
	@Override
	public void run() {
		for (int i = 0; i < 5; i++) {
			System.out.println("Runnableの実行中" + i);
		}
	}
}

MyRunnableTest.java

public class MyRunnableTest {
	public static void main(String[] args) {
		Runnable r = new MyRunnable();
		Thread t = new Thread(r);
		t.start();
		for (int i = 0; i < 5; i++) {
			System.out.println("mainの実行中" + i);
		}
	}
}

スレッドのライフサイクル

スレッドのライフサイクルは重要!(試験問題に出す!)

SleepThread.java

public class SleepThread extends Thread {
	@Override
	public void run() {
		try {
			System.out.println(getName() + " sleep前");
			Thread.sleep(3000);
			System.out.println(getName() + " sleep後");
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
}

SleepThreadTest.java

public class SleepThreadTest {

	public static void main(String[] args) {
		SleepThread t = new SleepThread();
		t.start();
		System.out.println("mainのEND");
	}

}

複数のスレッドを同時に起動するサンプル。
SleepThreadTest2.java

public class SleepThreadTest2 {

	public static void main(String[] args) {
		for (int i = 0; i < 3; i++) {
			SleepThread t = new SleepThread();
			t.start();
		}
		System.out.println("mainのEND");
	}

}

5月29日

テスト駆動開発で自動販売機を作る

例題はこちら⇒ TDDBC大阪

売上高を取得できるようにする

VendingMachineTest.java

	@Test
	public void testSale() {
		VendingMachine vm = new VendingMachine();
		int sale = vm.getSale();
		assertThat(sale, is(0));
		vm.throwInto(500);
		vm.purchace();
		sale = vm.getSale();
		assertThat(sale, is(120));
	}

売上高をインスタンス変数で保持する。
VendingMachine.java

public class VendingMachine {
	private int total = 0;
	private int stock = 5;
	private int sale = 0;

購入操作をしたときに売上高を増やし、そのインスタンス変数の値を返すようにする。
VendingMachine.java

	public Output purchace() {
		if (!isPurchacable()) {
			return new Output(0, 0);
		}
		total -= getJuicePrice();
		int change = total;
		total -= change;
		stock--;
		sale += getJuicePrice();
		return new Output(1, change);
	}

	public int getSale() {
		return sale;
	}

釣り銭を管理する

初期状態では、10円、50円、100円、500円の硬貨が、各10枚ずつ用意されている。

VendingMachineTest.java

	@Test
	public void testChangeStock() {
		VendingMachine vm = new VendingMachine();
		int s10 = vm.get10YenStock();
		assertThat(s10, is(10));
		int s50 = vm.get50YenStock();
		assertThat(s50, is(10));
		int s100 = vm.get100YenStock();
		assertThat(s100, is(10));
		int s500 = vm.get500YenStock();
		assertThat(s500, is(10));
	}

このテストにパスするコードを書く。
VendingMachine.java

	public int get10YenStock() {
		return 10;
	}

	public int get50YenStock() {
		return 10;
	}

	public int get100YenStock() {
		return 10;
	}

	public int get500YenStock() {
		return 10;
	}

150円を投入してジュースを買ったら、10円の釣り銭ストックが減る。
VendingMachineTest.java

	@Test
	public void testChangeStock() {
		VendingMachine vm = new VendingMachine();
		int s10 = vm.get10YenStock();
		assertThat(s10, is(10));
		int s50 = vm.get50YenStock();
		assertThat(s50, is(10));
		int s100 = vm.get100YenStock();
		assertThat(s100, is(10));
		int s500 = vm.get500YenStock();
		assertThat(s500, is(10));
		vm.throwInto(100);
		vm.throwInto(50);
		Output o = vm.purchace();
		s10 = vm.get10YenStock();
		assertThat(s10, is(7));
	}

購入するところで釣り銭のストックを減らす。
1回の購入なら残り7になるので、Fakeコードで実装する。

VendingMachine.java

	public Output purchace() {
		if (!isPurchacable()) {
			return new Output(0, 0);
		}
		total -= getJuicePrice();
		int change = total;
		decreaseStock(change);
		total -= change;
		stock--;
		sale += getJuicePrice();
		return new Output(1, change);
	}

	private void decreaseStock(int change) {
		stock10yen = 7;
	}

テストに2回目の購入を追加して、Fakeコードではテストにパスしないようにする。
VendingMachineTest.java

	@Test
	public void testChangeStock2() {
		VendingMachine vm = new VendingMachine();
		vm.throwInto(100);
		vm.throwInto(50);
		Output o = vm.purchace();
		assertThat(o.getJuiceCount(), is(1));
		assertThat(o.getChange(), is(30));
		int s10 = vm.get10YenStock();
		assertThat(s10, is(7));

		vm.throwInto(100);
		vm.throwInto(50);
		vm.purchace();
		s10 = vm.get10YenStock();
		assertThat(s10, is(4));
	}

お釣りに出す枚数だけ減らすように実装を修正する。
VendingMachine.java

	private void decreaseStock(int change) {
		stock10yen -= change / 10;
	}

200円を入れて購入したときのテストを用意する。
VendingMachineTest.java

	@Test
	public void testChangeStock3() {
		VendingMachine vm = new VendingMachine();
		vm.throwInto(100);
		vm.throwInto(100);
		vm.purchace();
		int s10 = vm.get10YenStock();
		assertThat(s10, is(7));
		int s50 = vm.get50YenStock();
		assertThat(s50, is(9));
	}

200円のテストにパスするコードを書く。
VendingMachine.java

	private void decreaseStock(int change) {
		if (change >= 50) {
			change -= 50;
			stock50yen--;
		}
		stock10yen -= change / 10;
	}

500円を入れて購入したときのテストを用意する。
VendingMachineTest.java

	@Test
	public void testChangeStock4() {
		VendingMachine vm = new VendingMachine();
		vm.throwInto(500);
		vm.purchace();
		int s10 = vm.get10YenStock();
		assertThat(s10, is(7));
		int s50 = vm.get50YenStock();
		assertThat(s50, is(9));
		int s100 = vm.get100YenStock();
		assertThat(s100, is(7));
	}

500円のテストにパスするコードを書く。
VendingMachine.java

	private void decreaseStock(int change) {
		if (change >= 100) {
			int c = change / 100;
			change -= c * 100;
			stock100yen -= c;
		}
		if (change >= 50) {
			change -= 50;
			stock50yen--;
		}
		stock10yen -= change / 10;
	}

1000円を入れて購入したときのテストを用意する。
VendingMachineTest.java

	@Test
	public void testChangeStock5() {
		VendingMachine vm = new VendingMachine();
		vm.throwInto(1000);
		vm.purchace();
		int s10 = vm.get10YenStock();
		assertThat(s10, is(7));
		int s50 = vm.get50YenStock();
		assertThat(s50, is(9));
		int s100 = vm.get100YenStock();
		assertThat(s100, is(7));
		int s500 = vm.get500YenStock();
		assertThat(s500, is(9));
	}

1000円のテストにパスするコードを書く。
VendingMachine.java

	private void decreaseStock(int change) {
		if (change >= 500) {
			change -= 500;
			stock500yen--;
		}
		if (change >= 100) {
			int c = change / 100;
			change -= c * 100;
			stock100yen -= c;
		}
		if (change >= 50) {
			change -= 50;
			stock50yen--;
		}
		stock10yen -= change / 10;
	}

10円玉の釣り銭を使い果たすテストを用意する。

VendingMachineTest.java

	@Test
	public void testChangeStock6() {
		VendingMachine vm = new VendingMachine();
		for (int i = 0; i < 3; i++) {
			vm.throwInto(100);
			vm.throwInto(50);
			vm.purchace();
		}
		int s10 = vm.get10YenStock();
		assertThat(s10, is(1));
		vm.throwInto(100);
		vm.throwInto(50);
		Output o = vm.purchace();
		s10 = vm.get10YenStock();
		assertThat(s10, is(1));
		assertThat(o.getJuiceCount(), is(0));
		assertThat(o.getChange(), is(0));
		int total = vm.getTotal();
		assertThat(total, is(150));
	}

5月25日

自動販売機をTDDで実装する

例題
TDDBC大阪

購入できるかどうかを調べるために、isPurchacable() メソッドを追加する。

	@Test
	public void testIsPurchacable() {
		VendingMachine vm = new VendingMachine();
		boolean b = vm.isPurchacable();
		assertThat(b, is(false));
	}

Eclipseでメソッドを生成するとテストをパスするようになる。

	public boolean isPurchacable() {
		return false;
	}

500円を投入すると購入可能になるテストを追加する。

	@Test
	public void testIsPurchacable() {
		VendingMachine vm = new VendingMachine();
		boolean b = vm.isPurchacable();
		assertThat(b, is(false));
		vm.throwInto(500);
		b = vm.isPurchacable();
		assertThat(b, is(true));
	}

自動販売機の購入の動作を実装する

お金を入れてボタンを押すとジュースとおつりが出てくる。

それっぽいテストを書いてみる。
出てくるもの(ジュースとおつり)をまとめて Output クラスとしておく。
VendingMachineTest.java

	@Test
	public void testPurchace() {
		VendingMachine vm = new VendingMachine();
		vm.throwInto(100);
		vm.throwInto(10);
		vm.throwInto(10);
		Output o = vm.purchace();
		assertThat(j, is(1));
		assertThat(change, is(0));
	}

Output.java

public class Output {
	private int juiceCount;
	private int change;

	public Output(int juiceCount, int change) {
		this.juiceCount = juiceCount;
		this.change = change;
	}

	public int getJuiceCount() {
		return juiceCount;
	}

	public int getChange() {
		return change;
	}
}

ここまでのコードは以下のとおり。
VendingMachineTest.java

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

import org.junit.Test;


public class VendingMachineTest {

	@Test
	public void testThrowInto() {
		VendingMachine vm = new VendingMachine();
		int change = vm.throwInto(10);
		assertThat(change, is(0));
		int total = vm.getTotal();
		assertThat(total, is(10));
		change = vm.throwInto(50);
		assertThat(change, is(0));
		total = vm.getTotal();
		assertThat(total, is(60));
	}

	@Test
	public void testThrowIntoNotAccept() {
		VendingMachine vm = new VendingMachine();
		int change = vm.throwInto(1);
		assertThat(change, is(1));
		int total = vm.getTotal();
		assertThat(total, is(0));
		change = vm.throwInto(5);
		assertThat(change, is(5));
		change = vm.throwInto(2000);
		assertThat(change, is(2000));
		change = vm.throwInto(5000);
		assertThat(change, is(5000));
		change = vm.throwInto(10000);
		assertThat(change, is(10000));
	}

	@Test
	public void testThrowIntoRefund() {
		VendingMachine vm = new VendingMachine();
		int change = vm.throwInto(10);
		assertThat(change, is(0));
		int total = vm.getTotal();
		assertThat(total, is(10));
		int refund = vm.refund();
		assertThat(refund, is(10));
		total = vm.getTotal();
		assertThat(total, is(0));
		change = vm.throwInto(100);
		assertThat(change, is(0));
		total = vm.getTotal();
		assertThat(total, is(100));
		change = vm.throwInto(500);
		assertThat(change, is(0));
		total = vm.getTotal();
		assertThat(total, is(600));
		refund = vm.refund();
		assertThat(refund, is(600));
	}

	@Test
	public void testGetJuice() {
		VendingMachine vm = new VendingMachine();
		String name = vm.getJuiceName();
		assertThat(name, is("コーラ"));
		int price = vm.getJuicePrice();
		assertThat(price, is(120));
		int stock = vm.getJuiceStock();
		assertThat(stock, is(5));
	}

	@Test
	public void testIsPurchacable() {
		VendingMachine vm = new VendingMachine();
		boolean b = vm.isPurchacable();
		assertThat(b, is(false));
		vm.throwInto(500);
		b = vm.isPurchacable();
		assertThat(b, is(true));
	}

	@Test
	public void testPurchace() {
		VendingMachine vm = new VendingMachine();
		vm.throwInto(100);
		vm.throwInto(10);
		vm.throwInto(10);
		Output o = vm.purchace();
		assertThat(o.getJuiceCount(), is(1));
		assertThat(o.getChange(), is(0));
		int total = vm.getTotal();
		assertThat(total, is(0));
	}
}

VendingMachine.java


public class VendingMachine {
	private int total;

	public int throwInto(int yen) {
		if (yen == 1) return 1;
		if (yen == 5) return 5;
		if (yen == 2000) return 2000;
		if (yen == 5000) return 5000;
		if (yen == 10000) return 10000;
		total += yen;
		return 0;
	}

	public int getTotal() {
		return total;
	}

	public int refund() {
		int refund = total;
		total = 0;
		return refund;
	}

	public String getJuiceName() {
		return "コーラ";
	}

	public int getJuicePrice() {
		return 120;
	}

	public int getJuiceStock() {
		return 5;
	}

	public boolean isPurchacable() {
		if (getJuiceStock() == 0) return false;
		return total >= getJuicePrice();
	}

	public Output purchace() {
		return new Output(1, 0);
	}
}

Juice.java


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;
	}
}

500円を投入して購入する。

VendingMachineTest.java

	@Test
	public void testPurchace500() {
		VendingMachine vm = new VendingMachine();
		vm.throwInto(500);
		Output o = vm.purchace();
		assertThat(o.getJuiceCount(), is(1));
		assertThat(o.getChange(), is(380));
		int total = vm.getTotal();
		assertThat(total, is(0));
	}

1000円を投入して購入する。

VendingMachineTest.java

	@Test
	public void testPurchace1000() {
		VendingMachine vm = new VendingMachine();
		vm.throwInto(1000);
		Output o = vm.purchace();
		assertThat(o.getJuiceCount(), is(1));
		assertThat(o.getChange(), is(880));
		int total = vm.getTotal();
		assertThat(total, is(0));
	}

在庫切れを実装する。
500円を投入して購入する。

VendingMachineTest.java

	@Test
	public void testPurchace500() {
		VendingMachine vm = new VendingMachine();
		vm.throwInto(1000);
		Output o = vm.purchace();
		assertThat(o.getJuiceCount(), is(1));
		assertThat(o.getChange(), is(880));
		int stock = vm.getJuiceStock();
		assertThat(stock, is(4));
		for (int i = 0; i < 4; i++) {
			vm.throwInto(500);
			o = vm.purchace();
		}
		stock = vm.getJuiceStock();
		assertThat(stock, is(0));
		vm.throwInto(500);
		o = vm.purchace();
		assertThat(o.getJuiceCount(), is(0));
		assertThat(o.getChange(), is(0));
		int total = vm.getTotal();
		assertThat(total, is(500));
	}

売上高を確認できるようにする。

VendingMachineTest.java

	@Test
	public void testSale() {
		VendingMachine vm = new VendingMachine();
		int sale = vm.getSale();
		assertThat(sale, is(0));
		vm.throwInto(500);
		vm.purchace();
		sale = vm.getSale();
		assertThat(sale, is(120));
	}

5月22日

TDD(テスト駆動開発)

JUnitを使ったTDDの練習。

FizzBuzz

FizzBuzzをTDDで作ってみる。

最初にテストコードを書く。
FizzBuzzTest.java

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

import org.junit.Test;

public class FizzBuzzTest {

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

そのテストコードをパスする最小限の実装をする。
FizzBuzz.java

public class FizzBuzz {
	public String say(int i) {
		return "1";
	}
}

テストコードを追加する。
FizzBuzzTest.java

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

そのテストコードをパスする最小限の実装をする。
FizzBuzz.java

public class FizzBuzz {
	public String say(int i) {
		if (i == 2) return "2";
		return "1";
	}
}

テストコードを追加する。
FizzBuzzTest.java

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

テストコードを追加→テストにパスするように実装 を繰り返す。

そのテストコードをパスする最小限の実装をする。
FizzBuzz.java

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

テストコードを追加する。
FizzBuzzTest.java

	@Test
	public void testSay4() {
		FizzBuzz fb = new FizzBuzz();
		String s = fb.say(4);
		assertThat(s, is("4"));
	}

数字を返すところは共通化できるのでリファクタリングする。

FizzBuzz.java

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

テストの追加、実装、リファクタリングを繰り返す。
最終的にはこんな感じのテストコードになる。

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

import org.junit.Test;


public class FizzBuzzTest {

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

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

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

	@Test
	public void testSay4() {
		FizzBuzz fb = new FizzBuzz();
		String s = fb.say(4);
		assertThat(s, is("4"));
	}

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

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

	@Test
	public void testSay78() {
		FizzBuzz fb = new FizzBuzz();
		String s = fb.say(7);
		assertThat(s, is("7"));
		s = fb.say(8);
		assertThat(s, is("8"));
	}

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

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

すべてのテストにパスするようになれば完成。
FizzBuzz.java


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

世界のナベアツ

世界のナベアツをTDDで作ってみる。

最初にテストコードを書く。
NabeatsuTest.java

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

import org.junit.Test;

public class NabeatsuTest {

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

そのテストコードをパスする最小限の実装をする。
Nabeatsu.java

public class Nabeatsu{
	public String say(int i) {
		return "1";
	}
}

FizzBuzzと同様にテストコードを追加する。

NabeatsuTest.java

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

import org.junit.Test;


public class NabeatsuTest {

	@Test
	public void testSay() {
		Nabeatsu fb = new Nabeatsu();
		String s = fb.say(1);
		assertThat(s, is("1"));
		s = fb.say(2);
		assertThat(s, is("2"));
	}

	@Test
	public void testSay3() {
		Nabeatsu fb = new Nabeatsu();
		String s = fb.say(3);
		assertThat(s, is("さぁーん"));
	}
}

テストにパスするコードを書く。
Nabeatsu.java


public class Nabeatsu {
	public String say(int i) {
		if (i == 3) return "さぁーん";
		return String.valueOf(i);
	}
}

最終的なテストコードはこんな感じ。

NabeatsuTest.java

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

import org.junit.Test;


public class NabeatsuTest {

	@Test
	public void testSay() {
		Nabeatsu fb = new Nabeatsu();
		String s = fb.say(1);
		assertThat(s, is("1"));
		s = fb.say(2);
		assertThat(s, is("2"));
	}

	@Test
	public void testSay3() {
		Nabeatsu fb = new Nabeatsu();
		String s = fb.say(3);
		assertThat(s, is("さぁーん"));
	}

	@Test
	public void testSay6() {
		Nabeatsu fb = new Nabeatsu();
		String s = fb.say(6);
		assertThat(s, is("さぁーん"));
	}

	@Test
	public void testSay13() {
		Nabeatsu fb = new Nabeatsu();
		String s = fb.say(13);
		assertThat(s, is("さぁーん"));
	}

	@Test
	public void testSay23() {
		Nabeatsu fb = new Nabeatsu();
		String s = fb.say(23);
		assertThat(s, is("さぁーん"));
	}

	@Test
	public void testSay31() {
		Nabeatsu fb = new Nabeatsu();
		String s = fb.say(31);
		assertThat(s, is("さぁーん"));
	}
}

すべてのテストにパスするコード。
Nabeatsu.java


public class Nabeatsu {
	public String say(int i) {
		String s = String.valueOf(i);
		if (s.contains("3")) return "さぁーん";
		if (i % 3 == 0) return "さぁーん";
		return String.valueOf(i);
	}
}

自動販売機を作る

TDDBCの例題で自動販売機があったので、この例題でTDDしてみる。
http://devtesting.jp/tddbc/?TDDBC%E5%A4%A7%E9%98%AA

まずは自動販売機のクラスを作る。
VendingMachine.java

public class VendingMachine {
}

お金を投入するメソッドを追加する。戻り値はお釣りの金額を返す。
VendingMachine.java

public class VendingMachine {
	public int throwInto(int yen) {
		return 0;
	}
}

テストコードを追加する。
VendingMachineTest.java

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

import org.junit.Test;

public class VendingMachineTest {

	@Test
	public void testThrowInto() {
		VendingMachine vm = new VendingMachine();
		int change = vm.throwInto(10);
		assertThat(change, is(0));
	}
}

テストにパスしたので、さらにテストコードを追加。
投入済みの金額を取得する getTotal() メソッドのテストを追加。
VendingMachineTest.java

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

import org.junit.Test;

public class VendingMachineTest {

	@Test
	public void testThrowInto() {
		VendingMachine vm = new VendingMachine();
		int change = vm.throwInto(10);
		assertThat(change, is(0));
		int total = vm.getTotal();
		assertThat(total, is(10));
	}
}

VendingMachine.java


public class VendingMachine {
	public int throwInto(int yen) {
		return 0;
	}

	public int getTotal() {
		return 0; // 0 → 10 にすればテストにパスする。
	}
}

さらにテストコードを追加。
お金を連続投入する。

VendingMachineTest.java

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

import org.junit.Test;

public class VendingMachineTest {

	@Test
	public void testThrowInto() {
		VendingMachine vm = new VendingMachine();
		int change = vm.throwInto(10);
		assertThat(change, is(0));
		int total = vm.getTotal();
		assertThat(total, is(10));
		change = vm.throwInto(50);
		assertThat(change, is(0));
		total = vm.getTotal();
		assertThat(total, is(60));
	}
}

連続投入したら投入済みの金額が合計されるようにする。
VendingMachine.java


public class VendingMachine {
	private int total;

	public int throwInto(int yen) {
		total += yen;
		return 0;
	}

	public int getTotal() {
		return total;
	}
}

受け入れないお金(1円・5円・1000円以外のお札)は、そのまま返却するテストを追加。
VendingMachineTest.java

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

import org.junit.Test;


public class VendingMachineTest {

	@Test
	public void testThrowInto() {
		VendingMachine vm = new VendingMachine();
		int change = vm.throwInto(10);
		assertThat(change, is(0));
		int total = vm.getTotal();
		assertThat(total, is(10));
		change = vm.throwInto(50);
		assertThat(change, is(0));
		total = vm.getTotal();
		assertThat(total, is(60));
	}

	@Test
	public void testThrowIntoNotAccept() {
		VendingMachine vm = new VendingMachine();
		int change = vm.throwInto(1);
		assertThat(change, is(1));
		int total = vm.getTotal();
		assertThat(total, is(0));
		change = vm.throwInto(5);
		assertThat(change, is(5));
		change = vm.throwInto(2000);
		assertThat(change, is(2000));
		change = vm.throwInto(5000);
		assertThat(change, is(5000));
		change = vm.throwInto(10000);
		assertThat(change, is(10000));
	}
}

テストにパスするように実装を追加する。
VendingMachine.java


public class VendingMachine {
	private int total;

	public int throwInto(int yen) {
		if (yen == 1) return 1;
		if (yen == 5) return 5;
		if (yen == 2000) return 2000;
		if (yen == 5000) return 5000;
		if (yen == 10000) return 10000;
		total += yen;
		return 0;
	}

	public int getTotal() {
		return total;
	}
}

返却ボタンを実装する。メソッド名を refund() とする。
まずはテストコードを書く。
VendingMachineTest.java

	@Test
	public void testThrowIntoRefund() {
		VendingMachine vm = new VendingMachine();
		int change = vm.throwInto(10);
		assertThat(change, is(0));
		int total = vm.getTotal();
		assertThat(total, is(0));
		int refund = vm.refund();
		assertThat(refund, is(10));
	}

refund() メソッドを追加する。
VendingMachine.java


public class VendingMachine {
	private int total;

	public int throwInto(int yen) {
		if (yen == 1) return 1;
		if (yen == 5) return 5;
		if (yen == 2000) return 2000;
		if (yen == 5000) return 5000;
		if (yen == 10000) return 10000;
		total += yen;
		return 0;
	}

	public int getTotal() {
		return total;
	}

	public int refund() {
		return 0; // 0 → 10 に修正すればテストにパスする
	}
}

返却のテストを追加する。
VendingMachineTest.java

	@Test
	public void testThrowIntoRefund() {
		VendingMachine vm = new VendingMachine();
		int change = vm.throwInto(10);
		assertThat(change, is(0));
		int total = vm.getTotal();
		assertThat(total, is(10));
		int refund = vm.refund();
		assertThat(refund, is(10));
		total = vm.getTotal();
		assertThat(total, is(0));
		change = vm.throwInto(100);
		assertThat(change, is(0));
		total = vm.getTotal();
		assertThat(total, is(100));
		change = vm.throwInto(500);
		assertThat(change, is(0));
		total = vm.getTotal();
		assertThat(total, is(600));
		refund = vm.refund();
		assertThat(refund, is(600));
	}

テストにパスするように実装を修正する。
VendingMachine.java


public class VendingMachine {
	private int total;

	public int throwInto(int yen) {
		if (yen == 1) return 1;
		if (yen == 5) return 5;
		if (yen == 2000) return 2000;
		if (yen == 5000) return 5000;
		if (yen == 10000) return 10000;
		total += yen;
		return 0;
	}

	public int getTotal() {
		return total;
	}

	public int refund() {
		int refund = total;
		total = 0;
		return refund;
	}
}

ジュースの管理をできるようにする。
ジュースの名前と値段と在庫数を取得できるようにする。
VendingMachineTest.java

	@Test
	public void testGetJuice() {
		VendingMachine vm = new VendingMachine();
		String name = vm.getJuiceName();
		assertThat(name, is("コーラ"));
		int price = vm.getJuicePrice();
		assertThat(price, is(120));
		int stock = vm.getJuiceStock();
		assertThat(stock, is(5));
	}

テストにパスするコードを書く。
VendingMachine.java


public class VendingMachine {
	private int total;

	public int throwInto(int yen) {
		if (yen == 1) return 1;
		if (yen == 5) return 5;
		if (yen == 2000) return 2000;
		if (yen == 5000) return 5000;
		if (yen == 10000) return 10000;
		total += yen;
		return 0;
	}

	public int getTotal() {
		return total;
	}

	public int refund() {
		int refund = total;
		total = 0;
		return refund;
	}

	public String getJuiceName() {
		return "コーラ";
	}

	public int getJuicePrice() {
		return 120;
	}

	public int getJuiceStock() {
		return 5;
	}
}