测试驱动运行阶段(运行所有 TestXXX 型的测试方法)
由图 7 所示 , 我们可以知道初始化完毕,即 testsuit() 创建好后 , 便进入方法 :
代码如下 :
图 14. doRun 函数代码

该方法为测试的驱动运行部分,结构如下:
·创建 TestResult 实例。
·将 junit.textui.TestRunner 的监听器 fPrinter 加入到 result 的监听器列表中。
其中,fPrinter 是 junit.textui.ResultPrinter 类的实例,该类提供了向控制台输出测试结果的一系列功能接口,输出的格式在类中定义。 ResultPrinter 类实现了 TestListener 接口,具体实现了 addError、addFailure、endTest 和 startTest 四个重要的方法,这种设计是 Observer 设计模式的体现,在 addListener 方法的代码中:
fListeners.addElement(listener);
}
将 ResultPrinter 对象加入到 TestResult 对象的监听器列表中,因此实质上 TestResult 对象可以有多个监听器显示测试结果。第三部分分析中将会描述对监听器的消息更新。
计时开始。
run(result) 测试运行。
计时结束。
统一输出,包括测试结果和所用时间。
其中最为重要的步骤为 run(result) 方法,代码如下。
图 15. run 函数代码

Junit 通过 for (Enumeration e= tests(); e.hasMoreElements(); ){ …… } 对 TestSuite 中的整个“树结构”递归遍历运行其中的节点和叶子。此处 JUnit 代码颇具说服力地说明了 Composite 模式的效力,run 接口方法的抽象具有重大意义,它实现了客户代码与复杂对象容器结构的解耦,让对象容器自己来实现自身的复杂结构,从而使得客户代码就像处理简单对象一样来处理复杂的对象容器。每次循环得到的节点 test,都同 result 一起传递给 runTest 方法,进行下一步更深入的运行。
图 16. junit.framework.TestResult.run 函数代码

这里变量 P 指向一个实现了 Protectable 接口的匿名类的实例,Protectable 接口只有一个 protect 待实现方法。而 junit.framework.TestResult.runProtected(Test, Protectable) 方法的定义为:
try {
p.protect();
}
catch (AssertionFailedError e) {
addFailure(test, e);
}
catch (ThreadDeath e) { // don't catch ThreadDeath by accident
throw e;
}
catch (Throwable e) {
addError(test, e);
}
}
可见 runProtected 方法实际上是调用了刚刚实现的 protect 方法,也就是调用了 test.runBare() 方法。另外,这里的 startTest 和 endTest 方法也是 Observer 设计模式中的两个重要的消息更新方法。
以下分析 junit.framework.TestCase.runBare() 方法:
图 17. junit.framework.TestCase.runBare() 函数代码

在该方法中,最终的测试会传递给一个 runTest 方法执行,注意此处的 runTest 方法是无参的,注意与之前形似的方法区别。该方法中也出现了经典的 setUp 方法和 tearDown 方法,追溯代码可知它们的定义为空。用户可以覆盖两者,进行一些 fixture 的自定义和搭建。 ( 注意:tearDown 放在了 finally{} 中,在测试异常抛出后仍会被执行到,因此它是被保证运行的。 )
主体工作还是在 junit.framework.TestCase.runTest() 方法中 , 代码如下 :
图 18. junit.framework.TestCase.runTest() 函数代码

该方法最根本的原理是:利用在图 13 中设定的 fName,借助 Reflection 机制,从 TestCase 中提取测试方法:
runMethod = getClass().getMethod(fName, (Class[]) null);
为每一个测试方法,创建一个方法对象 runMethod 并调用:
runMethod.invoke(this, (Object[]) new Class[0]);
只有在这里,用户测试方法的代码才开始被运行。
在测试方法运行时,众多的 Assert 方法会根据测试的实际情况,抛出失败异常或者错误。也是在“ runMethod.invoke(this, (Object[]) new Class[0]); ”这里,这些异常或错误往上逐层抛出,或者被某一层次处理,或者处理后再次抛出,依次递推,最终显示给用户。
流程图如下 :
图 19. JUnit 执行测试方法,并在测试结束后将失败和错误信息通知所有 test listener
