- JUnit 的概念及用途
JUnit 的设计精简,易学易用,但是功能却非常强大,这归因于它内部完善的代码结构。 Erich Gamma 是著名的 GOF 之一,因此 JUnit 中深深渗透了扩展性优良的设计模式思想。 JUnit 提供的 API 既可以让您写出测试结果明确的可重用单元测试用例,也提供了单元测试用例成批运行的功能。在已经实现的框架中,用户可以选择三种方式来显示测试结果,并且显示的方式本身也是可扩展的。看过junit的源码后,这里我就自己来做一个整理。这里以junit38为例,junit4X无非是将junit38中的一些约定换成了注解解析,这个后面我也会整理到。
- JUnit 基本原理
测试用例组成:
操作步骤:
将 B 通过命令行方式或图形界面选择方式传递给 R,R 自动运行测试,并显示结果。
我们先来看下junit源码中的目录结构和几个重要的类:
OK,我选中的几个类就是junit的核心类,我们依次来看下:
1,TestRunner:测试的执行器,每个测试用例的执行都是由这个类来运行的。该类有一个父类BaseTestRunner,这个父类实现了测试监听接口TestListener。TestListener代码如下:
package org.linkinpark.commons.framework; /** * @创建作者: LinkinPark * @创建时间: 2016年1月21日 * @功能描述: 监听器接口,所有的测试执行器都要实现该接口 */ public interface TestListener { /** * @创建时间: 2016年1月21日 * @相关参数: @param testt * @相关参数: @param e * @功能描述: 添加错误 */ public void addError(Test test, Throwable e); /** * @创建时间: 2016年1月21日 * @相关参数: @param test * @相关参数: @param e * @功能描述: 添加失败 */ public void addFailure(Test test, AssertionFailedError e); /** * @创建时间: 2016年1月21日 * @相关参数: @param test * @功能描述: 结束测试 */ public void endTest(Test test); /** * @创建时间: 2016年1月21日 * @相关参数: @param test * @功能描述: 开始测试 */ public void startTest(Test test); }
2,TestCase:测试用例。我们都知道使用junit都要继承该类,这个类在junit就表示一个测试用例。
该类继承Assert断言类,所以我们可以在自己写的测试中直接使用Assert类里面的各种断言。
该类实现Test接口,Test接口是所有的测试类都要去实现的接口,不管是测试实例TestCase还是测试组件TestSuite。这里贴出Test接口源码:
package org.linkinpark.commons.framework; /** * @创建作者: LinkinPark * @创建时间: 2016年1月21日 * @功能描述: 测试接口,所有的测试类都要实现这个接口 */ public interface Test { /** * @创建时间: 2016年1月21日 * @相关参数: @return * @功能描述: 测试用例执行的数量 */ public abstract int countTestCases(); /** * @创建时间: 2016年1月21日 * @相关参数: @param result * @功能描述: 开始执行一个测试用例然后+收集测试结果 */ public abstract void run(TestResult result); }
3,TestSuite:测试组件。junit中默认的测试容器,该类使用了Composite模式,使得junit有了多个用例一起执行的功能。
4,TestResult:测试结果。该类封装了一堆结果集,比如失败的,成功的用例个数等。该类被注入测试用例然后在里面被反射执行,同时收集测试结果。其实这个类也就是junit中观察者模式中的主题,他被注册了一系列的事件监听,他自己观察着测试用例的一些执行情况来第一时间通知监听器。这里贴出这个类的源码:
package org.linkinpark.commons.framework; import java.util.ArrayList; import java.util.Collections; import java.util.Enumeration; import java.util.List; /** * @创建作者: LinkinPark * @创建时间: 2016年1月21日 * @功能描述: 收集测试结果 * <p> * 区分了错误和失败2种情况 * </p> */ public class TestResult { protected List<TestFailure> fFailures; // 失败结果集 protected List<TestFailure> fErrors; // 错误结果集 protected List<TestListener> fListeners; // 测试监听 protected int fRunTests; // 执行的测试的数量 private boolean fStop; // 是否停止,开关 public TestResult() { fFailures = new ArrayList<TestFailure>(); fErrors = new ArrayList<TestFailure>(); fListeners = new ArrayList<TestListener>(); fRunTests = 0; fStop = false; } /** * @创建时间: 2016年1月21日 * @相关参数: @param test 测试用例 * @相关参数: @param e 异常 * @功能描述: 往错误List中添加错误 */ public synchronized void addError(Test test, Throwable e) { fErrors.add(new TestFailure(test, e)); for (TestListener each : cloneListeners()) { each.addError(test, e); } } /** * @创建时间: 2016年1月21日 * @相关参数: @param test 测试用例 * @相关参数: @param e 异常 * @功能描述: 往失败List中添加失败 */ public synchronized void addFailure(Test test, AssertionFailedError e) { fFailures.add(new TestFailure(test, e)); for (TestListener each : cloneListeners()) { each.addFailure(test, e); } } /** * @创建时间: 2016年1月21日 * @相关参数: @return listeners复制品 * @功能描述: 复制测试监听器List */ private synchronized List<TestListener> cloneListeners() { List<TestListener> result = new ArrayList<TestListener>(); result.addAll(fListeners); return result; } /** * @创建时间: 2016年1月21日 * @相关参数: @param listener 测试用例监听 * @功能描述: 注册一个事件 */ public synchronized void addListener(TestListener listener) { fListeners.add(listener); } /** * @创建时间: 2016年1月21日 * @相关参数: @param listener 测试用例监听 * @功能描述: 移除一个事件 */ public synchronized void removeListener(TestListener listener) { fListeners.remove(listener); } /** * @创建时间: 2016年1月21日 * @相关参数: @param test 测试用例 * @功能描述: 通知测试完成的结果。 */ public void endTest(Test test) { for (TestListener each : cloneListeners()) { each.endTest(test); } } /** * Returns an Enumeration for the errors */ /** * @创建时间: 2016年1月21日 * @相关参数: @return 错误迭代器 * @功能描述: 获取错误迭代器 */ public synchronized Enumeration<TestFailure> errors() { return Collections.enumeration(fErrors); } /** * @创建时间: 2016年1月21日 * @相关参数: @return 失败迭代器 * @功能描述: 获取失败迭代器 */ public synchronized Enumeration<TestFailure> failures() { return Collections.enumeration(fFailures); } /** * @创建时间: 2016年1月21日 * @相关参数: @param test * @功能描述: 运行一个测试用例,命令者模式 */ protected void run(final TestCase test) { startTest(test); Protectable p = new Protectable() { public void protect() throws Throwable { test.runBare(); } }; runProtected(test, p); endTest(test); } /** * @创建时间: 2016年1月21日 * @相关参数: @return * @功能描述: 获取执行的测试的数量 */ public synchronized int runCount() { return fRunTests; } /** * @创建时间: 2016年1月21日 * @相关参数: @param test * @相关参数: @param p * @功能描述: 运行一个用例 */ public void runProtected(final Test test, Protectable p) { 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); } } /** * @创建时间: 2016年1月21日 * @相关参数: @return * @功能描述: 检查是否应该停止了呢 */ public synchronized boolean shouldStop() { return fStop; } /** * Informs the result that a test will be started. */ /** * @创建时间: 2016年1月21日 * @相关参数: @param test * @功能描述: 通知结果:测试用例可以开始执行了 */ public void startTest(Test test) { final int count = test.countTestCases(); synchronized (this) { fRunTests += count; } for (TestListener each : cloneListeners()) { System.out.println("###########开始迭代运行整套测试,互相独立###########"); each.startTest(test); } } /** * @创建时间: 2016年1月21日 * @相关参数: * @功能描述: 标记运行的测试该停止了 */ public synchronized void stop() { fStop = true; } /** * @创建时间: 2016年1月21日 * @相关参数: @return * @功能描述: 判断测试是否成功 */ public synchronized boolean wasSuccessful() { return failureCount() == 0 && errorCount() == 0; } /** * @创建时间: 2016年1月21日 * @相关参数: @return 失败的数量 * @功能描述: 获取失败数量 */ public synchronized int failureCount() { return fFailures.size();<pre name="code" class="java">synchronized void print(TestResult result, long runTime) { printHeader(runTime); printErrors(result); printFailures(result); printFooter(result); }
}/** * @创建时间: 2016年1月21日 * @相关参数: @return 错误的数量 * @功能描述: 获取错误数量 */public synchronized int errorCount(){return fErrors.size();}}
温馨提示:上面的run方法也就是执行测试用例的那个方法用到了命令者模式(Java8中用了函数式接口来取代),将测试用例反射执行的这一整块业务代码封装到了TestCase类中,降低了代码耦合,而且也很符合对象建模,大家要好好体会。
5,ResultPrinter:结果打印类。该类是TestListener接口的一个实现,它封装了一个PrintStream输出流,用来向控制台打印一些测试结果。
synchronized void print(TestResult result, long runTime) { printHeader(runTime); printErrors(result); printFailures(result); printFooter(result); }
- JUnit 中的设计模式体现
GoF 的《设计模式》一书首次将设计模式提升到理论高度,并将之规范化。该书提出了 23 种基本设计模式,其后,在可复用面向对象软件的发展过程中,新的设计模式亦不断出现。关于设计模式的整理在我其他的博客系列里面,之后我看过spring
的源码之后我还会再次重新整理设计模式。
软件框架通常定义了应用体系的整体结构类和对象的关系等等设计参数,以便于具体应用实现者能集中精力于应用本身的特定细节。因此,设计模式有助于对框架结构的理解,成熟的框架通常使用了多种设计模式,JUnit 就是其中的优秀代表。设计模式是 JUnit 代码的精髓,没有设计模式,JUnit 代码无法达到在小代码量下的高扩展性。总体上看,有三种设计模式在 JUnit 设计中得到充分体现,分别为 Composite 模式、Command 模式以及 Observer 模式。
OK,junit源码核心类就先整理到这里。接下来我会整理junit完整的生命周期。