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 个阶段:初始化阶段.运行阶段和结果捕捉阶段. 这篇 ...
随机推荐
- Java学习笔记23(Calendar类)
Calendar意味日历,对Date类中的很多方法做了改进 Calendar类是一个抽象类,不可以见对象,需要子类完成实现 不过这个类有特殊之处,不需要创建子类对象,而是使用它的静态方法直接获取: 示 ...
- windows虚拟内存管理
内存管理是操作系统非常重要的部分,处理器每一次的升级都会给内存管理方式带来巨大的变化,向早期的8086cpu的分段式管理,到后来的80x86 系列的32位cpu推出的保护模式和段页式管理.在应用程序中 ...
- CSS中垂直居中的方法
昨天总结了css中水平居中的方法,今天来总结一下css中实现垂直居中的方法. line-height line-height用于实现单行文本的垂直居中,如下图中,我们要求单行文本垂直居中,只需要将di ...
- islider结合react的简单实用
我用islider都是结合react来使用,主要运用在移动端,做首页轮播图,或者是手机图片预览,左右滑动 首先需要 npm install islider.js --save 让后在jsx文件头部引入 ...
- Webpack+vue2.0如何注册全局组件 (01)
Part 1, 问题: webpack + vue2.0框架中,如何在入口js中注册组件? 就是在一个月以前,匆匆闯入vuejs这个社群,基本了解了vuejs的一些基础特性和语法.笔者兴致勃勃地开始想 ...
- Debian9 配置之旅
注:在安装的过程中,要选择网络镜像,不然要出大问题...(我选择了网易163的源) 注:下面的操作发生在我apt-get update,更新出现了错误,做的处理. _Stretch_ - Offici ...
- jq手风琴效果
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- Zabbix实战-简易教程系列
一.基础篇(安装和接入) Zabbix实战-简易教程--总流程 Zabbix实战-简易教程--整体架构图 Zabbix实战-简易教程--DB安装和表分区 Zabbix实战-简易教程--Server端 ...
- 我们编写 React 组件的最佳实践
刚接触 React 的时候,在一个又一个的教程上面看到很多种编写组件的方法,尽管那时候 React 框架已经相当成熟,但是并没有一个固定的规则去规范我们去写代码. 在过去的一年里,我们在不断的完善我们 ...
- Cnm%(个人模版)
Cnm%: #include<stdio.h> #include<string.h> #include<vector> using namespace std; # ...