JavaでTDDに挑戦してみる(N+2回目

前回の記事はこちら

参考にさせてもらってるのはこちら


さて、前回はテストケースNo.3まで進むことができました。

No.クラス名ピックアップした数字期待結果
115, 5, 330FizzBuzz
215, 545FizzBuzz
315, 315FizzBuzz
45, 360Buzz
51575FizzBuzz
6510Buzz
739Fizz
8上記以外7171
9111
10100100100
FizzBuzzのテストケース。期待値含む

今日はNo.4から進めてみたいと思います。

No.4 60でBuzzと表示させる

No.1, 2, 3まで全てFizzBuzzと表示させるところまできました。
こんどは初めて、Buzzと表示させるところまで進めてみたいと思います。

変更を加えるまでのコード

本コードはこちら

まずはテストコードを加えてみます。テストコードの一番下に変更を加えてみました。

もちろんレッドです、が、ここで大きな問題があることに気づきました。
60でBuzzを表示、と思っていたのですが、よく考えてみたら60は15の倍数ではありませんか。
テスト結果で、それが分かりました。

いかんいかん、慌ててテストケースを変更します。60ではなく、50にしてみましょう。

No.クラス名ピックアップした数字期待結果
115, 5, 330FizzBuzz
215, 545FizzBuzz
315, 315FizzBuzz
45, 360 –> 50Buzz
51575FizzBuzz
6510Buzz
739Fizz
8上記以外7171
9111
10100100100
テストケースNo.4のテスト条件を、60から50に変更した

テストコードを変更します。

テストを走らせると、相変わらず赤いバーが表示されますが、ある意味で正解ということに。

さて、それでは本コードに変更を加えてみます。

テストを走らせると、緑のバーになりました。

5の倍数が正しいんだけどなー、と思いつつ、次へと向かいます。

No.5 75でFizzBuzzと表示させる

これはすでに、15の倍数でFizzBuzzを表示させるように本コードを作っているので、テストコードを加えるだけで緑になるはず。

テストコードはこちら

この状態でテストを走らせると、はい、ちゃんと緑になりました。

レッド→グリーン→リファクタリングの精神に則ると、少々気持ち悪いのではありますが、ここではリファクタリングを不要とするのかな、と思います。
なぜなら「15の倍数はFizzBuzzと返す」ところはすでに完成しており、これ以上リファクタリングする要素が見当たらないからです。
こちら。

もしかすると、テストケースが過剰なのでは?という思いに駆られます。
テストケースを見てみるとNo. 1, 2, 3, 5の4つのケースでFizzBuzzを期待結果とおいているのですが、これがやりすぎなんじゃないかと。
組み合わせを推測して、そっち目線だと適切だと思うのですが、ここは実際のシステム開発では予算が十分にあり、テストを実行するためのリソース(要員)が足りているという前提であれば、この4ケースは活かしても良いのでしょう。ただ現実的にはギリギリの予算でQA分を積んでいることもあるので、ここは優先度をつけて、例えばNo.5だけ優先度を下げ、時間があればやる、みたいな方法を取っても良いのかもしれません。

No.6 10でBuzzと表示させる

ここもNo.5のように、No.4のBuzzと同じ期待値を取ることが見えています。
一方、まだここでは本コードに「5の倍数の時はBuzzとする」とは書いていないので、リファクタリングの機会が訪れているように思います。

早速テストコードを。

だいぶテストコードが長くなってきました。

赤いバーが出ます。

本コードを変更してみます。とりあえず5の倍数という形ではなく、10の場合に限定して書いてみます。

テストを走らせてみると、はい、緑のバーになりました。

それではここで、リファクタリングをしてみたいと思います。
赤い枠で囲んだところを、5の倍数という形ですね。

変更してみます。

テストを走らせてみます。緑のまま。よかった。

これで、5の倍数に関するリファクタリングは終わりです。

No.7 9でFizzと表示させる

さぁ、今度は初めてFizzと表示させるものです。この辺まで来ると細かな説明は不要ですね。

テストコード

実施結果

本コードを変更

テスト結果は緑。気持ちが良い!

Fizzはリファクタリングしなくて良いのかな、と少し悩みます。経験則として「2つ目のFizzが来た時に、合わせてリファクタリングすれば良い」とBuzzの時に覚えました。
が、残念なことに、テストケースでは2つ目のFizzがありません。
これでは、9以外の、3の倍数の時にはFizzではなく、その数字をそのまま返してしまうことになります。
つまりバグ。しかも残念なことに、テストケースからは発見されません。上記の、10個のテストケースしか用意していないから。

テストケースには無いのですが、試しに、12を入力した時のバーを見てみましょう。

テストコード

テストした結果

赤いバーが出ます。12を入れたら、Fizzではなく12が返ってきてしまっています。
これでは要件通りの仕様にならず、です。
しょうがないのでテストケースに追加します。

No.クラス名ピックアップした数字期待結果
115, 5, 330FizzBuzz
215, 545FizzBuzz
315, 315FizzBuzz
45, 360 –> 50Buzz
51575FizzBuzz
6510Buzz
739Fizz
8上記以外7171
9111
10100100100
111212Fizz
テストケースNo.11を追加した

No.7からNo.11に飛んでしまいました。が、これは必要なことです。
前回の記事では、期待結果が同じものは、1つにまとめられるんじゃない?説がありましたが、こんな落とし穴があるとは。QAを数十年やってる身として、身に染みる思いです。

さて、気を取り直して、No.11を完成させてみたいと思います。まずは緑にしてみましょう。

本コード

テストを走らせた結果

そしてリファクタリング。Fizzとなっている2箇所(上記参照)を、3の倍数に置き換えてみます。

実際にはここで、本コードは完成ですね。以下、No.8〜No.10を一気に進めてみたいと思います。

No.8 71で71を表示させる

テストコード

テスト実施前の赤いバーを、と思いましたがこちらはすでに本コード内で処理ができるようになっているため、緑のバーは出てきません。いきなり緑。

No.9 1で1を表示させる

ここもNo.8と同様、すでに処理ができるようになっています。ただここは境界値として、一応テストコードを作ってみます。

テストコード

緑のバー

つぎはいよいよ最後です。

No.10 100で100を表示させる

テストコード

緑のバー、と思いきや!テストケースにミスがありました。

100で100を、ではなく、これは5の倍数になるため、Buzzが表示されるということです。

またしてもテストケースにミスがありました。。カッコ悪い。。100を境界値としてテストをするのであれば、これは100ではなく、Buzzが返ってくるのが正しいですね。

修正したテストケースはこちら。

No.クラス名ピックアップした数字期待結果
115, 5, 330FizzBuzz
215, 545FizzBuzz
315, 315FizzBuzz
45, 360 –> 50Buzz
51575FizzBuzz
6510Buzz
739Fizz
8上記以外7171
9111
10100100100 –> Buzz
111212Fizz
テストケースNo.10の期待結果を変更した

100でBuzzと返ってくるように変更したテストコードはこちら

最後の、緑のバー。

なにはともあれ、これで全部のテストケースをクリアすることができました。

振り返り

途中、冗長な説明になってしまいましたが、これで全てのテストを実施することができました。
こうやって見てみると、最初に考えていたテストケースに間違いがあったり、足りない部分があったりして、要件だけを見てみても、抜けるところがあるんだなー、という印象です。
Fizzを期待値とするテストケースは、2つ以上必要ということも分かりました。これは意外。
テストの現場にいる身としては、なるべく少ないケース数をこなすように鍛えられているため、今回のように実際の本コードを見ながらテストしてみると、その、なるべく少なく、というところはきちんと確認をして作り上げるべきなんだなという気持ちにさせられました。

一方、TDDの入り口をこの一連の活動を通して見ることができたのは、とても有意義でした。
私にJavaのスキルがもっとあれば、もうちょい複雑なプログラムを書くことができるんだろうなと思いつつ、Cucumberを使ってテストコードを書き、それに合わせて本コードに追加、そしてリファクタリングという流れが見えたのは、大きな一歩です。ケント・ベックさんの本を何度か読んではみましたが、ただ読むだけではなく、実際にやってみることで、それが実感できた印象です。
ここで終わりにするのではなく、もうちょい複雑なJavaのコードを使いつつ、また改めてTDDをやってみたいとおもいます。

ご参考まで、コードはこちらです

本コード

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

テストコード

import static org.junit.jupiter.api.Assertions.*;

import org.junit.jupiter.api.Test;

public class FizzBuzzTest {
	@Test
	public void 引数に30を与えたらFizzBuzzを返す() {
		FizzBuzz fizzbuzz = new FizzBuzz();
		assertEquals("FizzBuzz", fizzbuzz.response(30));
	}
	
	@Test
	public void 引数に45を加えたらFizzBuzzを返す() {
		FizzBuzz fizzbuzz = new FizzBuzz();
		assertEquals("FizzBuzz", fizzbuzz.response(45));
	}
	
	@Test
	public void 引数に15を加えたらFizzBuzzを返す() {
		FizzBuzz fizzbuzz = new FizzBuzz();
		assertEquals("FizzBuzz", fizzbuzz.response(15));
	}
	
	@Test
	public void 引数に50を加えたらBuzzを返す() {
		FizzBuzz fizzbuzz = new FizzBuzz();
		assertEquals("Buzz", fizzbuzz.response(50));
	}
	
	@Test
	public void 引数に75を加えたらFizzBuzzを返す() {
		FizzBuzz fizzbuzz = new FizzBuzz();
		assertEquals("FizzBuzz", fizzbuzz.response(75));
	}
	
	@Test
	public void 引数に10を加えたらBuzzを返す() {
		FizzBuzz fizzbuzz = new FizzBuzz();
		assertEquals("Buzz", fizzbuzz.response(10));
	}
	
	@Test
	public void 引数に9を加えたらFizzを返す() {
		FizzBuzz fizzbuzz = new FizzBuzz();
		assertEquals("Fizz", fizzbuzz.response(9));
	}
	
	@Test
	public void 引数に12を加えたらFizzを返す() {
		FizzBuzz fizzbuzz = new FizzBuzz();
		assertEquals("Fizz", fizzbuzz.response(12));
		
	}
	
	@Test
	public void 引数に71を加えたら71を返す() {
		FizzBuzz fizzbuzz = new FizzBuzz();
		assertEquals("71", fizzbuzz.response(71));
	}
	
	@Test
	public void 引数に1を加えたら1を返す() {
		FizzBuzz fizzbuzz = new FizzBuzz();
		assertEquals("1", fizzbuzz.response(1));
	}
	
	@Test
	public void 引数に100を加えたらBuzzを返す() {
		FizzBuzz fizzbuzz = new FizzBuzz();
		assertEquals("Buzz", fizzbuzz.response(100));
	}
	
}