输入下述内容作为TestCase子类的main方法:
public static void main(String[] argv) {
junit.textui.TestRunner.run(suite());
}
如果你需要的话,用junit.awtui.TestRunner或junit.swingui.TestRunner代替junit.textui.TestRunner。
方便起见,示例文件中提供了CalculatorTest类的另一个版本CalculatorTest.java.v3,它包含suite方法和前面描述的main方法,当然,你在使用之前需要把它改名为CalculatorTest.java。如果你使用的环境是Linux,或者是Cygwin在Windows下模拟的UNIX,你可以用diff命令查看不同版本之间的差别,这在学习新东西时通常很有用。例如,输入diff CalculatorTest.java.v2 CalculatorTest.java.v3可以查看CalculatorTest.java.v2和CalculatorTest.java.v3之间的区别。
编译新的CalculatorTest类之后,可以运行了。这次不必输入java junit.textui.TestRunner CalculatorTest,可以用java CalculatorTest代替。
十、在程序和测试中添加功能(Adding Functionality to Your Application and to Your Test)
现在假如说你要在FactorCalculator中添加功能。测试驱动的开发方式建议你首先增加测试,验证测试失败,(代码还没写当然失败,译者注。)然后再编写新功能的代码,并确保测试通过。现在假如你要增加一个求大公约数的方法,名字叫“gcd”。(此处省略20字,译者注)可以用下面三条验证:6和4的大公约数是2。36和18的大公约数是18。30和75的大公约数是15。
刚才那些都是正常的例子。除此之外,你还应该测试边缘和错误的情况,例如gcd(2, 1)和gcd(3, -1)。下面的代码可以做这些测试:
程序列表 5:
public void testGcd() {
assertEquals("bad value for gcd(6, 4)", 2, calc.gcd(6, 4));
assertEquals("bad value for gcd(36, 18)", 18, calc.gcd(36, 18));
assertEquals("bad value for gcd(30, 75)", 15, calc.gcd(30, 75));
assertEquals("bad value for gcd(2, 1)", 1, calc.gcd(2, 1));
try {
calc.gcd(3, -1);
fail("gcd should throw exception for when either argument is less than 1");
} catch (IllegalArgumentException e) {
// do nothing because throwing IAE is the proper action
}
}
把程序列表5中的代码添加到CalculatorTest.java。如果你不想敲键盘,你可以从示例代码中拷贝CalculatorTest.java.v4,把名字改成CalculatorTest.java。
为了让CalculatorTest能编译,需要在FactorCalculator中添加一个stub,你在CalculatorTest中调用了gcd方法,你必须在FactorCalculator中定义gcd方法。把下述内容加到FactorCalculator中:
public int gcd(int a, int b) {
return 1;
}
如果你不想敲键盘,你可以从示例代码中拷贝FactorCalculator.java.v3,把名字改成FactorCalculator.java。
很明显,大部分情况下上面的gcd方法都返回错误结果,但测试驱动的开发方式信奉“先让它出错,然后再纠正它”( "errors first, then correction of errors")为有效的开发模式。测试代码和其它代码一样,也可能出错。有可能你的测试代码不能发现程序中的错误。如果你在开发程序功能前编写测试代码,你可以确保测试正确发现错误,因此减少错误被疏漏的机会。
输入javac *.java编译FactorCalculator和CalculatorTest类。在编译和运行时都需要确保JUnit库在classpath中。输入java CalculatorTest。你将看到下面的输出:
....F
Time: 0.01
There was 1 failure:
1) testGcd(CalculatorTest)junit.framework.AssertionFailedError: bad value for gcd(6, 4) expected:<2> but was:<1>
at CalculatorTest.testGcd(CalculatorTest.java:125)
...
at CalculatorTest.main(CalculatorTest.java:14)
FAILURES!!!
Tests run: 4, Failures: 1, Errors: 0
开始测试失败了,这是意料之中的结果,也正是我们想要的。如果它不失败,那意味着测试的设计或实现出了问题。JUnit会把你给它的信息显示出来,所以应该写有意义的错误信息。现在修复FactorCalculator类,让它通过测试。删除FactorCalculator类gcd方法中的“return 1;”一行,用下面的代码代替:
程序列表6 (functional implementation of gcd method):
int gcd = 1;
int smallerInt = (a < b) ? a : b;
for(int i = smallerInt; i > 1; i--) {
if (isDivisor(a, i) && isDivisor(b, i)) {
gcd = i;
break;
}
}
return gcd;
如果你不想敲键盘,你可以从示例代码中拷贝FactorCalculator.java.v4,把名字改成FactorCalculator.java。
输入javac FactorCalculator.java重新编译javac FactorCalculator。输入java CalculatorTest重跑测试。你仍然得到错误,因为参数错误时gcd方法没有抛出异常。调用gcd(3, -1)应该产生一个IllegalArgumentException,但事实上没有。把下面的代码加到gcd方法的前面可以解决这个问题。
if ((a < 1) || (b < 1)) {
throw new IllegalArgumentException();
}
修改后的FactorCalculator是示例代码中的FactorCalculator.java.v5,你可以更名为FactorCalculator.java。重新编译FactorCalculator.java后运行测试。一切正常,测试通过,状态报告类似:
....
Time: 0.008
OK (4 tests)
十一、总结(Conclusion)
现在你已经知道如何用JUnit进行单元测试了,在你自己的代码中进行试验吧,亲身体会一下程序测试的好处。
现在准备进入测试驱动开发系列的下一章。下一章,也是五个部分中的第三部分,将带你进入如何在EJB容器中测试服务器端EJB组件的操作细节。