OK,前面的博客我们整理了junit运行完了所有的测试用例,那么OK了,现在开始该收集测试结果了。

在这最后一步中,junit主要是玩一个类,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;
}



前面框架在执行测试用例的过程中,用了命令者模式,调用runProtected真正开始执行test.runBare()方法。我们来看下这个runProtected()方法源码:

/**
* @创建时间: 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);
}
}

这里的try块触发测试用例的执行,然后2个catch块来捕获异常。如果抛出了AssertionFialedError异常,那么就说明我们的语言失败了,然后添加到TestResult类的失败结果集中,如果抛出了其他异常,那么就是说我们编写的测试代码报错了,然后添加到TestResult类的错误结果集中。JUnit 执行测试方法,并在测试结束后将失败和错误信息通知给所有的 test listener 。其中 addFailure、addError、endTest、startTest 是 TestListener
接口的四大方法,而 TestListener 涉及到 Observer 设计模式。

/**
* @创建时间: 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);
}
}

上面的代码不多分析了,又是观察者模式来开始通知TestResult类上注册的监听器。在Junit38中往TestResult类中默认添加的监听其实就一个ResultPrinter类。TestResult 的 addFailure 进一步调用 ResultPrinter 的 addFailure。

@Override
public void addError(Test test, Throwable e)
{
getWriter().println("KAO,有报错啦!!!");
} @Override
public void addFailure(Test test, AssertionFailedError t)
{
getWriter().println("KAO,有失败啦");
}

此处代码将产生的失败对象加入到了 fFailures,将错误对象加入到fErrors中,此处的结果在程序退出时作为测试总体成功或失败的判断依据。而在 for 循环中,TestResult 对象循环遍历观察者(监听器)列表,通过调用相应的更新方法,更新所有的观察者信息,这部分代码也是整个 Observer 设计模式架构的重要部分。根据以上描述,JUnit 采用 Observer 设计模式使得 TestResult 与众多测试结果监听器通过接口 TestListenner 达到松耦合,使 JUnit 可以支持不同的使用方式。目标对象(TestResult)不必关心有多少对象对自身注册,它只是根据列表通知所有观察者。因此,TestResult
不用更改自身代码,而轻易地支持了类似于 ResultPrinter 这种监听器的无限扩充。目前,已有文本界面、图形界面和 Eclipse 集成组件三种监听器,用户完全可以开发符合接口的更强大的监听器。

有一个东西也值得我们学习,出于安全考虑,cloneListeners() 使用克隆机制取出监听器列表:

/**
* @创建时间: 2016年1月21日
* @相关参数: @return listeners复制品
* @功能描述: 复制测试监听器List
*/
private synchronized List<TestListener> cloneListeners()
{
List<TestListener> result = new ArrayList<TestListener>();
result.addAll(fListeners);
return result;
}

OK,现在测试用例都执行完了,我们返回到最初的TestRunner类中,看看测试执行器的运行测试用例3大步的最后一步。

/**
* @创建时间: 2016年1月22日
* @相关参数: @param suite
* @相关参数: @param wait
* @相关参数: @return
* @功能描述: 测试执行器执行测试
*/
public TestResult doRun(Test suite, boolean wait)
{
TestResult result = createTestResult();
result.addListener(fPrinter);
long startTime = System.currentTimeMillis();
suite.run(result);
// 收集结果
long endTime = System.currentTimeMillis();
long runTime = endTime - startTime;
fPrinter.print(result, runTime);
pause(wait);
return result;
}

计算了开始时间和结束时候,取他们差值就是运行测试用例的执行时间,然后调用ResultPrinter类型的属性fPrinter来开始打印结果。打印结果又分4步;打印耗时-->打印错误-->打印失败-->打印测试个数,失败个数,错误个数。

synchronized void print(TestResult result, long runTime)
{
printHeader(runTime);
printErrors(result);
printFailures(result);
printFooter(result);
}
protected void printHeader(long runTime)
{
getWriter().println("第四步:框架开始统计时间====");
getWriter().println("耗时:" + elapsedTimeAsString(runTime) + "秒");
} /**
* @创建时间: 2016年1月21日
* @相关参数: @param runTime
* @相关参数: @return
* @功能描述: 格式化时间,单位毫秒
*/
protected String elapsedTimeAsString(long runTime)
{
return NumberFormat.getInstance().format((double) runTime / 1000);
} protected void printErrors(TestResult result)
{
printDefects(result.errors(), result.errorCount(), "错误");
} protected void printFailures(TestResult result)
{
printDefects(result.failures(), result.failureCount(), "失败");
} protected void printDefects(Enumeration<TestFailure> booBoos, int count, String type)
{
if (count == 0)
return;
if (count == 1)
{
getWriter().println("遗憾:!一共有" + count + "个" + type + ":");
}
else
{
getWriter().println("遗憾:!一共有 " + count + "个" + type + ":");
}
for (int i = 1; booBoos.hasMoreElements(); i++)
{
printDefect(booBoos.nextElement(), i);
}
} protected void printFooter(TestResult result)
{
getWriter().println("第五步:框架开始统计结果====");
if (result.wasSuccessful())
{
getWriter().println("结果:OK,木问题!");
// getWriter().println(" (" + result.runCount() + " test" + (result.runCount() == 1 ? "" : "s") + ")");
getWriter().println("统计:一共执行了" + result.runCount() + "个测试用例");
}
else
{
getWriter().println("结果:AU,出错啦!");
getWriter().println("Tests run: " + result.runCount() + ", Failures: " + result.failureCount() + ", Errors: " + result.errorCount());
}
getWriter().println("第六步:框架结束整个测试====");
}



在printFooter()这个方法中统计统计个数的时候,成功与否取决于TestTesult类。如果失败次数和错误次数都是0,那么测试成功。

/**
* @创建时间: 2016年1月21日
* @相关参数: @return
* @功能描述: 判断测试是否成功
*/
public synchronized boolean wasSuccessful()
{
return failureCount() == 0 && errorCount() == 0;
}

这里有2个类值得我们研究下。

1,AssertionFailedError 异常类。

package org.linkinpark.commons.framework;

/**
* @创建作者: LinkinPark
* @创建时间: 2016年1月21日
* @功能描述: 某个预言失败抛出该异常
*
* <p>
* AssertionError:抛出该异常指示某个断言失败。
* </p>
*/
public class AssertionFailedError extends AssertionError
{ private static final long serialVersionUID = 1L; public AssertionFailedError()
{
} /**
* @创建时间: 2016年1月21日
* @相关参数: @param message 详细的错误信息。
* @构造描述: 如果错误信息为null就return空字符串。
*/
public AssertionFailedError(String message)
{
super(defaultString(message));
} /**
* @创建时间: 2016年1月21日
* @相关参数: @param message 详细的错误信息。
* @相关参数: @return 如果错误信息为null就return空字符串。
* @功能描述: 空值校验+处理错误信息
*/
private static String defaultString(String message)
{
return message == null ? "" : message;
}
}

2,TestFailure 测试失败类。

package org.linkinpark.commons.framework;

import java.io.PrintWriter;
import java.io.StringWriter; /**
* @创建作者: LinkinPark
* @创建时间: 2016年1月21日
* @功能描述: 失败的测试用例容器
*/
public class TestFailure
{
protected Test fFailedTest; // 失败的测试
protected Throwable fThrownException; // 异常信息 public TestFailure(Test failedTest, Throwable thrownException)
{
fFailedTest = failedTest;
fThrownException = thrownException;
} public Test failedTest()
{
return fFailedTest;
} public Throwable thrownException()
{
return fThrownException;
} /**
* 重写toString(),返回一个错误原因描述
*/
@Override
public String toString()
{
return fFailedTest + ": " + fThrownException.getMessage();
} /**
* 获取错误栈信息,期间打印到控制台
*/
public String trace()
{
StringWriter stringWriter = new StringWriter();
PrintWriter writer = new PrintWriter(stringWriter);
thrownException().printStackTrace(writer);
return stringWriter.toString();
} /**
* 获取错误描述
*/
public String exceptionMessage()
{
return thrownException().getMessage();
} /**
* Returns {@code true} if the error is considered a failure
* (i.e. if it is an instance of {@code AssertionFailedError}),
* {@code false} otherwise.
*/
public boolean isFailure()
{
return thrownException() instanceof AssertionFailedError;
}
}

好了,现在junit运行测试3大步都已经走完了,我们返回最初的测试入口,来看下最后程序的退出。勿忘初心,方得始终。

public static final int SUCCESS_EXIT = 0;
public static final int FAILURE_EXIT = 1;
public static final int EXCEPTION_EXIT = 2;
/**
* @创建时间: 2016年1月21日
* @相关参数: @param args
* @功能描述: 测试框架入口
*/
public static void main(String[] args)
{
TestRunner aTestRunner = new TestRunner();
try
{
String[] linkinArgs = new String[] { "org.linkinpark.commons.textui.LinkinTestAll" };
TestResult testResult = aTestRunner.start(linkinArgs);
if (!testResult.wasSuccessful())
{
System.exit(FAILURE_EXIT);
}
System.exit(SUCCESS_EXIT);
}
catch (Exception e)
{
System.err.println(e.getMessage());
System.exit(EXCEPTION_EXIT);
}
}

框架最终如何退出同样取决于TestResult类的失败个数和错误个数,如果wasSuccessful那么没有问题,直接正常退出就好了,否则的话异常退出。最后以System类的exit api结束这篇博客。

===========================================================================================================================================================================================

exit

public static void exit(int status)
终止当前正在运行的 Java 虚拟机。参数用作状态码;根据惯例,非 0 的状态码表示异常终止。

该方法调用 Runtime 类中的 exit 方法。该方法永远不会正常返回。

调用 System.exit(n) 实际上等效于调用:

 Runtime.getRuntime().exit(n)
 
参数:
status - 退出状态。
抛出:
SecurityException - 如果安全管理器存在并且其 checkExit 方法不允许以指定状态退出。
另请参见:
Runtime.exit(int)

===========================================================================================================================================================================================

junit源码解析--捕获测试结果的更多相关文章

  1. junit源码解析总结

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

  2. 机器学习实战(Machine Learning in Action)学习笔记————03.决策树原理、源码解析及测试

    机器学习实战(Machine Learning in Action)学习笔记————03.决策树原理.源码解析及测试 关键字:决策树.python.源码解析.测试作者:米仓山下时间:2018-10-2 ...

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

    JUnit 的概念及用途 JUnit 是由 Erich Gamma 和 Kent Beck 编写的一个开源的单元测试框架.它属于白盒测试,只要将待测类继承 TestCase 类,就可以利用 JUnit ...

  4. junit源码解析--测试驱动运行阶段

    前面的博客里面我们已经整理了junit的初始化阶段,接下来就是junit的测试驱动运行阶段,也就是运行所有的testXXX方法.OK,现在我们开始吧. 前面初始化junit之后,开始执行doRun方法 ...

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

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

  6. Tensorflow版Faster RCNN源码解析(TFFRCNN) (2)推断(测试)过程不使用RPN时代码运行流程

    本blog为github上CharlesShang/TFFRCNN版源码解析系列代码笔记第二篇   推断(测试)过程不使用RPN时代码运行流程 作者:Jiang Wu  原文见:https://hom ...

  7. jQuery整体架构源码解析(转载)

    jQuery整体架构源码解析 最近一直在研读 jQuery 源码,初看源码一头雾水毫无头绪,真正静下心来细看写的真是精妙,让你感叹代码之美. 其结构明晰,高内聚.低耦合,兼具优秀的性能与便利的扩展性, ...

  8. jQuery整体架构源码解析

    最近一直在研读 jQuery 源码,初看源码一头雾水毫无头绪,真正静下心来细看写的真是精妙,让你感叹代码之美. 其结构明晰,高内聚.低耦合,兼具优秀的性能与便利的扩展性,在浏览器的兼容性(功能缺陷.渐 ...

  9. JUnit源码分析 - 扩展 - 自定义RunListener

    RunListener简述 JUnit4中的RunListener类用来监听测试执行的各个阶段,由RunNotifier通知测试去运行.RunListener与RunNotifier之间的协作应用的是 ...

随机推荐

  1. SQLAlchemy基础操作一

    用前安装 pip3 install sqlalchemy ORM ORM就是运用面向对象的知识,将数据库中的每个表对应一个类,将数据库表中的记录对应一个类的对象.将复杂的sql语句转换成类和对象的操作 ...

  2. Android查缺补漏(View篇)--自定义View利器Canvas和Paint详解

    上篇文章介绍了自定义View的创建流程,从宏观上给出了一个自定义View的创建步骤,本篇是上一篇文章的延续,介绍了自定义View中两个必不可少的工具Canvas和Paint,从细节上更进一步的讲解自定 ...

  3. K:二叉树

    相关介绍:  二叉树是一种特殊的树,它的每个节点最多只有两棵子树,并且这两棵子树也是二叉树.由于二叉树中的两棵子树有左右之分,为此,二叉树是有序树. 二叉树的定义:  二叉树是由n(n>=0)个 ...

  4. JavaScrip:Function函数编程

    自定义函数定义 1.函数通过function关键字创建,函数创建格式: function 函数名称([参数,...]){ 代码段; return 返回值; } 注意事项: 函数名称不要包含特殊字符 函 ...

  5. <魔域>按键精灵脚本

    用了三天时间才写完,实现了通过图片识别读取坐标数值,自动寻路等简单功能. 主要的难点在于游戏中的坐标系,和电脑屏幕的坐标系存在三维旋转关系,难以换算. //全局变量:第一个数左上右下坐标 Global ...

  6. 【C#学习笔记之一】C#中的关键字

    C#中的关键字 关键字是对编译器具有特殊意义的预定义保留标识符.它们不能在程序中用作标识符,除非它们有一个 @ 前缀.例如,@if 是有效的标识符,但 if 不是,因为 if 是关键字. 下面是列出的 ...

  7. BZOJ:4820: [Sdoi2017]硬币游戏&&BZOJ:1444: [Jsoi2009]有趣的游戏(高斯消元求概率)

    1444: [Jsoi2009]有趣的游戏 4820: [Sdoi2017]硬币游戏 这两道题都是关于不断随机生成字符后求出现给定字符串的概率的问题. 第一题数据范围较小,将串建成AC自动机以后,以A ...

  8. POJ 2370 Democracy in danger(简单贪心)

    Democracy in danger Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 3388   Accepted: 25 ...

  9. Playrix Codescapes Cup (Codeforces Round #413, rated, Div. 1 + Div. 2)(A.暴力,B.优先队列,C.dp乱搞)

    A. Carrot Cakes time limit per test:1 second memory limit per test:256 megabytes input:standard inpu ...

  10. Codeforces Round #416 (Div. 2)(A,思维题,暴力,B,思维题,暴力)

    A. Vladik and Courtesy time limit per test:2 seconds memory limit per test:256 megabytes input:stand ...