junit源码解析--核心类
- JUnit 的概念及用途
JUnit 是由 Erich Gamma 和 Kent Beck 编写的一个开源的单元测试框架。它属于白盒测试,只要将待测类继承 TestCase 类,就可以利用 JUnit 的一系列机制进行便捷的自动测试了。
JUnit 的设计精简,易学易用,但是功能却非常强大,这归因于它内部完善的代码结构。 Erich Gamma 是著名的 GOF 之一,因此 JUnit 中深深渗透了扩展性优良的设计模式思想。 JUnit 提供的 API 既可以让您写出测试结果明确的可重用单元测试用例,也提供了单元测试用例成批运行的功能。在已经实现的框架中,用户可以选择三种方式来显示测试结果,并且显示的方式本身也是可扩展的。看过junit的源码后,这里我就自己来做一个整理。这里以junit38为例,junit4X无非是将junit38中的一些约定换成了注解解析,这个后面我也会整理到。
- JUnit 基本原理
一个 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 中的设计模式体现
设计模式(Design pattern)是一套被反复使用的、为众人所知的分类编目的代码设计经验总结。使用设计模式是为了可重用和扩展代码,增加代码的逻辑性和可靠性。设计模式的出现使代码的编制真正工程化,成为软件工程的基石。
GoF 的《设计模式》一书首次将设计模式提升到理论高度,并将之规范化。该书提出了 23 种基本设计模式,其后,在可复用面向对象软件的发展过程中,新的设计模式亦不断出现。关于设计模式的整理在我其他的博客系列里面,之后我看过spring
的源码之后我还会再次重新整理设计模式。
软件框架通常定义了应用体系的整体结构类和对象的关系等等设计参数,以便于具体应用实现者能集中精力于应用本身的特定细节。因此,设计模式有助于对框架结构的理解,成熟的框架通常使用了多种设计模式,JUnit 就是其中的优秀代表。设计模式是 JUnit 代码的精髓,没有设计模式,JUnit 代码无法达到在小代码量下的高扩展性。总体上看,有三种设计模式在 JUnit 设计中得到充分体现,分别为 Composite 模式、Command 模式以及
Observer 模式。
OK,junit源码核心类就先整理到这里。接下来我会整理junit完整的生命周期。
junit源码解析--核心类的更多相关文章
- lucene原理及源码解析--核心类
马云说:大家还没搞清PC时代的时候,移动互联网来了,还没搞清移动互联网的时候,大数据时代来了. 然而,我看到的是:在PC时代搞PC的,移动互联网时代搞移动互联网的,大数据时代搞大数据的,都是同一伙儿人 ...
- Spring源码解析——核心类介绍
前言: Spring用了这么久,虽然Spring的两大核心:IOC和AOP一直在用,但是始终没有搞懂Spring内部是怎么去实现的,于是决定撸一把Spring源码,前前后后也看了有两边,很多东西看了就 ...
- Log4j源码解析--核心类解析
原文出处:http://www.blogjava.net/DLevin/archive/2012/06/28/381667.html.感谢上善若水的无私分享. 在简单的介绍了Log4J各个模块类的作用 ...
- Spring源码解析-核心类之XmlBeanDefinitionReader
XmlBeanDefinitionReader XML配置文件的读取是 Spring 中重要的功能,因为 Spring 的大部分功能都是以配置作为切入点的,那么我们可以从 XmlBeanDefinit ...
- Spring源码解析-核心类之XmlBeanFactory 、DefaultListableBeanFactory
DefaultListableBeanFactory XmlBeanFactory 继承自 DefaultListableBeanFactory , 而 DefaultListableBeanFact ...
- junit源码解析总结
前面的博客我们也已经整理到了,我们使用junit38,在写测试类的时候我们的测试类必须继承TestCase.这个所有测试类的父类在junit.framework包下面. 前面我们的整理都是说直接在ID ...
- Mybatis 系列7-结合源码解析核心CRUD 配置及用法
[Mybatis 系列10-结合源码解析mybatis 执行流程] [Mybatis 系列9-强大的动态sql 语句] [Mybatis 系列8-结合源码解析select.resultMap的用法] ...
- [Java源码解析] -- String类的compareTo(String otherString)方法的源码解析
String类下的compareTo(String otherString)方法的源码解析 一. 前言 近日研究了一下String类的一些方法, 通过查看源码, 对一些常用的方法也有了更透彻的认识, ...
- junit源码解析--初始化阶段
OK,我们接着上篇整理.上篇博客中已经列出的junit的几个核心的类,这里我们开始整理junit完整的生命周期. JUnit 的完整生命周期分为 3 个阶段:初始化阶段.运行阶段和结果捕捉阶段. 这篇 ...
随机推荐
- Python Tornado篇
Tornado既是一个web server,也是web framework.而它作为web server 采用的是asynchronous IO的网络模型,这是一种很高效的模型. Tornado 和现 ...
- SP3精密星历简介
IGS精密星历采用sp3格式,其存储方式为ASCII文本文件,内容包括表头信息以及文件体,文件体中每隔15 min给出1个卫星的位置,有时还给出卫星的速度.它的特点就是提供卫星精确的轨道位置.采样率为 ...
- VirtualBox - RTR3InitEx failed with rc=-1912 (rc=-1912)
有一天重启电脑后虚拟机virtual box突然打不开了,提示类似 https://askubuntu.com/questions/900794/virtualbox-rtr3initex-faile ...
- Angular整合zTree
1 前提准备 1.1 新建一个angular4项目 参考博文:点击前往 1.2 去zTree官网下载zTree zTree官网:点击前往 三少使用的版本:点击前往 2 编程步骤 从打印出zTree对象 ...
- JavaScript数组forEach()、map()、reduce()方法
1. js 数组循环遍历. 数组循环变量,最先想到的就是 for(var i=0;i<count;i++)这样的方式了. 除此之外,也可以使用较简便的forEach 方式 2. forEac ...
- MySQL(九)之数据表的查询详解(SELECT语法)一
这一篇是MySQL中的重点也是相对于MySQL中比较难得地方,个人觉得要好好的去归类,并多去练一下题目.MySQL的查询也是在笔试中必有的题目.希望我的这篇博客能帮助到大家! 重感冒下的我,很难受!k ...
- HDU 1017 A Mathematical Curiosity【水,坑】
A Mathematical Curiosity Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java ...
- Codeforces 626C Block Towers(二分)
C. Block Towers time limit per test:2 seconds memory limit per test:256 megabytes input:standard inp ...
- javascript之事件监听
addEventListener是一个监听事件并处理相应的函数,用于向指定元素添加事件句柄,可使用removeEventListener()方法来移除addEventListener()方法添加的事件 ...
- mysql下优化表和修复表命令使用说明(REPAIR TABLE和OPTIMIZE TABLE)
REPAIR TABLE `table_name` 修复表 OPTIMIZE TABLE `table_name` 优化表 show create table tablename 表结构 REPA ...