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. http转https实战教程iis7.5

    HTTP转HTTPS实战教程IIS7.5 (备注:确保IIS安装完成.ASP.NET 等配置无误) 1.          本文以阿里云为例,先在阿里云注册域名并且进行备案.备案完成后,在左侧菜单申请 ...

  2. msgpack库的神奇用法

    一般来说,我们会把头部和实际消息分开定义,因为内部工作的worker之间发送消息有些额外的字段,这些字段不属于实际的消息.这时候我们会把worker消息中一个字段定义为interface{}或者obj ...

  3. kylin客户端(python编写)不能按照预期的segment进行rebuild

    kylin_client_tool 提供了对cube进行BUILD,REBUILD,MERGE功能,其中REBUILD却不能达到预期的效果按照指定的segment执行. 场景: 当我在kylin we ...

  4. Codeforces #448 Div2 E

    #448 Div2 E 题意 给出一个数组,有两种类型操作: 选定不相交的两个区间,分别随机挑选一个数,交换位置. 查询区间和的期望. 分析 线段树区间更新区间求和. 既然是涉及到两个区间,那么对于第 ...

  5. 【ASP.NET Core】在Win 10 的 Linux 子系统中安装 .NET Core

    在上一篇文章中,老周扯了一下在 Windows 10 中开启 Linux 子系统,并且进行了一些简单的设置.本篇咱们就往上面安装 .net core . 老周假设你从来没有用过 Linux,所以,接着 ...

  6. Progressive Web Applications

    Progressive Web Applications take advantage of new technologies to bring the best of mobile sites an ...

  7. 我们编写 React 组件的最佳实践

    刚接触 React 的时候,在一个又一个的教程上面看到很多种编写组件的方法,尽管那时候 React 框架已经相当成熟,但是并没有一个固定的规则去规范我们去写代码. 在过去的一年里,我们在不断的完善我们 ...

  8. TSP(个人模版)

    O(n^2)TSP: #include<stdio.h> #include<string.h> #include<algorithm> #include<io ...

  9. 微积分入门("SX"T版)

    哎,微积分,表示暂时并没有很深入的研究……虽然高中有教,但是好像跟小西瓜学的顺序不太一样,嗯……教微积分之前不应该把极限学下来么……不管了,本文按傻X腾的理解来搞吧. 极限……大学的东西喔,我们先来认 ...

  10. HDU 1847 Good Luck in CET-4 Everybody!(规律,博弈)

    Good Luck in CET-4 Everybody! Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K ...