测试技术培训-TDD-FizzBuzz

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


测试技术培训-TDD-FizzBuzz_第1张图片
image.png

然后是定义本地变量


测试技术培训-TDD-FizzBuzz_第2张图片
image.png

于是得到第一行代码
      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/
       * 

* 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-FizzBuzz_第3张图片
image.png

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


测试技术培训-TDD-FizzBuzz_第4张图片
image.png

可以看到,通过TDD出来的代码,天然就达到了很高的代码覆盖,这也是TDD的一个优势。
测试技术培训-TDD-FizzBuzz_第5张图片
image.png

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

你可能感兴趣的:(测试技术培训-TDD-FizzBuzz)