• 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源码解析--核心类的更多相关文章

  1. lucene原理及源码解析--核心类

    马云说:大家还没搞清PC时代的时候,移动互联网来了,还没搞清移动互联网的时候,大数据时代来了. 然而,我看到的是:在PC时代搞PC的,移动互联网时代搞移动互联网的,大数据时代搞大数据的,都是同一伙儿人 ...

  2. Spring源码解析——核心类介绍

    前言: Spring用了这么久,虽然Spring的两大核心:IOC和AOP一直在用,但是始终没有搞懂Spring内部是怎么去实现的,于是决定撸一把Spring源码,前前后后也看了有两边,很多东西看了就 ...

  3. Log4j源码解析--核心类解析

    原文出处:http://www.blogjava.net/DLevin/archive/2012/06/28/381667.html.感谢上善若水的无私分享. 在简单的介绍了Log4J各个模块类的作用 ...

  4. Spring源码解析-核心类之XmlBeanDefinitionReader

    XmlBeanDefinitionReader XML配置文件的读取是 Spring 中重要的功能,因为 Spring 的大部分功能都是以配置作为切入点的,那么我们可以从 XmlBeanDefinit ...

  5. Spring源码解析-核心类之XmlBeanFactory 、DefaultListableBeanFactory

    DefaultListableBeanFactory XmlBeanFactory 继承自 DefaultListableBeanFactory , 而 DefaultListableBeanFact ...

  6. junit源码解析总结

    前面的博客我们也已经整理到了,我们使用junit38,在写测试类的时候我们的测试类必须继承TestCase.这个所有测试类的父类在junit.framework包下面. 前面我们的整理都是说直接在ID ...

  7. Mybatis 系列7-结合源码解析核心CRUD 配置及用法

    [Mybatis 系列10-结合源码解析mybatis 执行流程] [Mybatis 系列9-强大的动态sql 语句] [Mybatis 系列8-结合源码解析select.resultMap的用法] ...

  8. [Java源码解析] -- String类的compareTo(String otherString)方法的源码解析

    String类下的compareTo(String otherString)方法的源码解析 一. 前言 近日研究了一下String类的一些方法, 通过查看源码, 对一些常用的方法也有了更透彻的认识,  ...

  9. junit源码解析--初始化阶段

    OK,我们接着上篇整理.上篇博客中已经列出的junit的几个核心的类,这里我们开始整理junit完整的生命周期. JUnit 的完整生命周期分为 3 个阶段:初始化阶段.运行阶段和结果捕捉阶段. 这篇 ...

随机推荐

  1. virtuoso数据库的安装方法

    数据库virtuoso有两种安装配置方式 第一种就是直接在系统中默认安装,拷贝virtuoso的安装文件,直接默认安装. 第二种是配置安装方式 参考地址:http://vos.openlinksw.c ...

  2. 【树状数组】BZOJ3132 上帝造题的七分钟

    3132: 上帝造题的七分钟 Time Limit: 20 Sec  Memory Limit: 128 MBSubmit: 1004  Solved: 445[Submit][Status][Dis ...

  3. 【dfs】POJ2386湖计数

    Lake Counting Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 34735   Accepted: 17246 D ...

  4. PE文件详解(八)

    本文转载自小甲鱼PE文件详解系列教程原文传送门 当应用程序需要调用DLL中的函数时,会由系统将DLL中的函数映射到程序的虚拟内存中,dll中本身没有自己的栈,它是借用的应用程序的栈,这样当dll中出现 ...

  5. Swagger入门教程

    [译]5.41 Swagger tutorial 单击此处查看原文 更多概念参见:Implementing Swagger with your API docs 关于 Swagger Swagger能 ...

  6. .NET(c#) 移动开发平台 - Smobiler(1)

    如果说基于.net的移动开发平台,目前比较流行的可能是xamarin了,不过除了这个,还有一个比xamarin更好用的国内的.net移动开发平台,smobiler,不用学习另外一套开发模式或者搭建复杂 ...

  7. HyperLedger Fabric基于zookeeper和kafka集群配置解析

    简述 在搭建HyperLedger Fabric环境的过程中,我们会用到一个configtx.yaml文件(可参考Hyperledger Fabric 1.0 从零开始(八)--Fabric多节点集群 ...

  8. LevelDB的源码阅读(二) Open操作

    在Linux上leveldb的安装和使用中我们写了一个测试代码,内容如下: #include "leveldb/db.h" #include <cassert> #in ...

  9. 软件安装之-------VM虚拟机安装windows系统

    一 准备工作 1 电脑已经安装上VMware Workstation 2 一个Windows系统,下载纯净版系统可到(www.itellyou.cn下载) 3  软碟通 下载可到(http://dow ...

  10. Spring Boot快速入门(一): Hello Spring Boot

    原文地址:https://lierabbit.cn/articles/2 一.准备工作 java环境:jdk 1.8 开发工具:idea 二.创建项目 打开idea 点击Create New Proj ...