1 什么是FizzBuzz
FizzBuzz是一个非常适合各种场景使用的编程题目,也被各路大佬推荐用于TDD的教学。具体的FizzBuzz题目描述可参见
http://codingdojo.org/kata/FizzBuzz/
需求Product backlog
/**
*
* 从1至100依次报数,
* 如第1位报“1”,
* 第2位报“2”
* 如果碰到被3整除的数则报“Fizz”
* 如果碰到被5整除的数则报“Buzz”
* 如果同时被3和5整除则报“FizzBuzz”
*/
FizzBuzz-利用IDEA协助TDD
我们首先定义一个TestFizzBuzz的测试类,并写下以下代码
fizzBuzz = new FizzBuzz();
然后Alt+Enter快捷键调出新建类FizzBuzz

然后是定义本地变量

于是得到第一行代码
FizzBuzz fizzBuzz = new FizzBuzz();
以及空的类FizzBuzz
2FizzBuzz的TDD实现过程
FizzBuzz- 第一个用例:第一个人报数1
现在我们编写第一个用例,用来覆盖Backlog中的第一项。
public class TestFizzBuzz {
FizzBuzz fizzBuzz = new FizzBuzz();
@Test
public void TestOneIsOne() {
assertThat(fizzBuzz.say(1)).isEqualTo("1");
}
这个用例用来验证,第一个人报数为1。
于是,我们可以用以下代码来实现say方法
public String say(int i) {
return "1";
}
第一个用例就通过了。
FizzBuzz- 第二个用例:第二个人报数2
@Test
public void TestTwoIsTwo() {
assertThat(fizzBuzz.say(2)).isEqualTo("2");
}
这个时候如果我们执行测试,第二个用例会失败。因为say方法只是简单地return 了1。为了能让这两个用例都通过,需要修改say方法的实现如下:
public String say(int i) {
return Integer.toString(i);
}
于是,Junit的绿条条又出现了。
FizzBuzz- 第三个用例:被3整除的数则报“Fizz”
于是又有了第三个用例
@Test
public void TestThreeIsFizz() {
assertThat(fizzBuzz.say(3)).isEqualTo("Fizz");
}
当然,这个时候如果执行测试,这个用例会失败,因为程序的返回结果是“3”。
于是,测试用例驱动我们去修改say的实现
public String say(int i) {
if(i%3==0)
return "Fizz";
return Integer.toString(i);
}
增加了i是否能被3整除的判断。
FizzBuzz- 第四个用例:被5整除的数则报“Buzz”
类似的,我们继续编写5的倍数的用例和实现代码,此处先略过。
FizzBuzz- 第五个用例:被15整除的数则报“FizzBuzz”
能够被三和五都整除的数,也就是15的倍数。代码类似,只是从执行顺序上来说,这个判断需要放置在针对3和5的判断之前。
FizzBuzz完整实现
以下是FizzBuzz的完整实现,
package com.github.tdd;
public class FizzBuzz {
public String say(int i) {
if(i%15==0)
return "FizzBuzz";
if(i%3==0)
return "Fizz";
if(i%5==0)
return "Buzz";
return Integer.toString(i);
}
}
以及驱动我们实现上述代码的测试用例
package com.github.tdd;
import org.junit.Test;
import static org.assertj.core.api.Assertions.*;
public class TestFizzBuzz {
/**
* http://codingdojo.org/kata/FizzBuzz/
* <p>
* Product backlog
* 从1至100依次报数,如第1位报“1”,第2位报“2”
* 如果碰到被3整除的数则报“Fizz”
* 如果碰到被5整除的数则报“Buzz”
* 如果同时被3和5整除则报“FizzBuzz”
*/
FizzBuzz fizzBuzz = new FizzBuzz();
@Test
public void TestOneIsOne() {
assertThat(fizzBuzz.say(1)).isEqualTo("1");
}
@Test
public void TestTwoIsTwo() {
assertThat(fizzBuzz.say(2)).isEqualTo("2");
}
@Test
public void TestThreeIsFizz() {
assertThat(fizzBuzz.say(3)).isEqualTo("Fizz");
}
@Test
public void TestFiveIsFuzz() {
assertThat(fizzBuzz.say(5)).isEqualTo("Buzz");
}
@Test
public void Test15IsFizzFuzz() {
assertThat(fizzBuzz.say(15)).isEqualTo("FizzBuzz");
}
}
3用例执行结果
我们来看下完整的用例执行结果

五个用例全部通过了。再看下代码覆盖率

可以看到,通过TDD出来的代码,天然就达到了很高的代码覆盖,这也是TDD的一个优势。

4问题
通过TDD出来的代码,还会有缺陷么?
当然会有。作为经验丰富的测试人员,您估计已经在浏览代码时想到了,如果给say方法输入一个0,结果会如何?在Backlog中并没有明确,但是程序还是需要处理的,譬如抛出一个异常。
通过TDD出来的代码,还需要重构么?
当然需要。TDD的过程本身就是不断重构实现代码的过程。而且目前编写出来的代码,也只是对Backlog的一个简单实现,当然存在很多的优化空间,虽然只是简简单单的8行。
如果您对上述代码有何重构建议或者想到了什么测试用例,欢迎留言。
PS.
IDEA快捷键参考:
Alt +Enter: Create Class
Ctrl+Alt+B:Goto Implementation
assertJ断言参考:
https://assertj.github.io/doc/#assertj-overview
网友评论