TDD(Test Driven Development)名为测试驱动开发,但实际上是一种很好的驱动软件设计演进的方法。
源于敏捷开发
测试驱动开发、JUnit,极限编程(XP)系出同源,都是源自 Kent Beck 大师。
通过密集的单元测试、自动化测试,为软件建立测试保护网,让开发团队在可靠的基础上快速迭代,持续交付。
每一个小的变更,都经过高覆盖自动化(单元)测试的检验。
从设计测试开始
有的人不能理解在没有实现的情况下就能编写出测试用例,这其实不是关键。
先写一个测试还是后写一个测试是一个次要问题,关键在于 1)是自动化的测试;2)测试失败要修复。
不论何时,都要想着写下的代码是否通过自动化测试来守护,如果不能则进行重构。
在如此的思想下,最佳的实践办法就演变成了先写测试后实现代码,这样就能强迫人们优先思考如何开展自动化测试。
案例分析
拿最简单的 Fizz Buzz 举例
功能:输出从 1 到 n 数字的字符串表示。
如果 n 是 3 的倍数,输出“Fizz”;
如果 n 是 5 的倍数,输出“Buzz”;
如果 n 同时是 3 和 5 的倍数,输出 “FizzBuzz”。
上述需求直接很容易编写代码
public class FizzBuzz{
public static void main(String[] args) {
new FizzBuzz().fizzBuzz(Integer.parseInt(args[0]));
}
void fizzBuzz(int n) {
for (int i = 1; i <= n; i++) {
boolean n是3的倍数 = i % 3 == 0;
boolean n是5的倍数 = i % 5 == 0;
if (n是3的倍数 && n是5的倍数) {
System.out.println("FizzBuzz");
} else if (n是3的倍数) {
System.out.println("Fizz");
} else if (n是5的倍数) {
System.out.println("Buzz");
} else {
System.out.println(i);
}
}
}
}
在这种情况下,去编写自动化测试用例就很困难:1)难以断言,2)难以隔离出离散的用例。
但如果使用 TDD 讨论,第一步,应该创建一个什么样的用例?很容易就会想到要先验证输入 1 输出 1
public class FizzBuzzTest {
FizzBuzz2 f = new FizzBuzz2();
@Test
public void when_1_then_1() {
String result = f.fizzBuzz(1);
assertThat(result, is("1"));
}
}
于是写下
……
String fizzBuzz(int i) {
return Integer.toString(i);
}
……
然后在其上逐渐的长出完整的代码,并且很容易测试。