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