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 个阶段:初始化阶段.运行阶段和结果捕捉阶段. 这篇 ...
随机推荐
- 大公司的资深工程师和小公司的Leader如何决择?
很多人在技术的道路上,都会面临选择,一个是大公司的资深工程师/技术专家,一个是小公司的leader,这个选择是一条分叉路口,是持续纵向深入发展,还是横向发展.这实际上就是个人职业规划问题. 接着往专家 ...
- Kylin与CDH兼容性剖析
1. 概述 Apache Kylin™是一个开源的分布式分析引擎,提供Hadoop之上的SQL查询接口及多维分析(OLAP)能力以支持超大规模数据,最初由eBay Inc. 开发并贡献至开源社区.它能 ...
- Swagger入门教程
[译]5.41 Swagger tutorial 单击此处查看原文 更多概念参见:Implementing Swagger with your API docs 关于 Swagger Swagger能 ...
- C#学习笔记-适配器模式
什么是适配器模式? 适配器模式(Adapter):将一个类的接口转换成客户希望的另外一个接口. Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作. 什么时候运用适配器模式? ...
- 【Java】Java中BigDecimal的基本运算
BigDecimal一共有4个够造方法,让来看看其中比较常用的两种用法: 第一种:BigDecimal(double val)Translates a double into a BigDecimal ...
- linux(三)之linux常用命令二
今天就是星期五了,又可以休息两天了.有点小激动,开心.不过还是要加油,因为还有很多东西等着我去学习呢! 七.chmod 作用:修改文件的权限 7.1.命令格式:chmod mode filename ...
- SSAS属性中更改AllowedBrowsingFolders的值后才能更改其它文件夹的值
首先 以管理员身份运行 打开SQL Server Management Studio (SSMS). 在Sql Server Analysis Service中的属性中有很多文件夹属性,决定了存放CU ...
- 如何为图片添加热点链接?(map + area)
所谓图片热点链接就是为图片指定一个或多个区域以实现点击跳转到指定的页面.简单来说就是点击某一区域就能跳转到相应的页面,而无需点击整个图片才能跳转. 说到图片热点链接,我首先想到了map + area, ...
- C语言课程设计大整数运算
该大整数运算系统用于对有符号的位数不超过500位的大整数进行加.减.乘.除四则运算和计算N(0<=N<=10000)的阶乘.注意事项 : 1.操作期间,进行四则运算时若大整数为正数请 ...
- MySQL的Illegal mix of collationsy异常原因和解决方法
原创 2008年12月25日 11:54:00 标签: mysql / collation / character / variables / database / server 今天在使用数据库 ...