写在前面:本文基于Junit3.8.1版本,因为这是我第一次进行源码学习,先从简单的源码开始学起

1. 示例代码

1.1 准备工作

  • 下载Junit3.8.1的JAR包
  • 需要下载junit-3.8.1-sources.jarjunit-3.8.1.jar,前者是源码包,后者是项目中需要使用的Jar包;

1.2 项目中导入Junit相关jar包

  • 使用Eclipse,新建项目test;
  • test右键,选中 properties

1.3 编写示例代码

  • JUnit4之前,JUnit明确要求测试方法名以"test"开头;
  • JUnit 测试类需要继承 "TestCase";
// SampleCalculator.java,需要测试的类
public class SampleCalculator{
// 加法
private int add(int a, int b){
return a + b;
} // 乘法
public int multiply(int a, int b){
return a * b;
} //除法
public double divide(int a, int b){
return a/b;
}
} // 编写测试类,该类需要继承 TestCase
public class TestSample extends TestCase{ private SampleCalculator cal = null; protected void setUp() throws Exception{
super.setUp();
// 每个测试方法执行前,重新new一个对象,避免测试用例之间的依赖
cal = new SampleCalculator():
System.out.println("在每个测试方法执行前执行 ---setUp...");
} // 测试源代码的私有方法
public void testAdd(){
System.out.println("测试方法testAdd...");
try{
// 使用反射,进行私有方法的调用
Class<SampleCalculator> clazz = SampleCalculator.class;
Method method = clazz.getDeclaredMethod("add", new Class[] { Integer.TYPE, Integer.TYPE});
method.setAccessible(true);
Object obj = method.invoke(cal, new Object[]{1, 2});
Assert.assertEquals(3, obj);
}catch(Exception e){
Assert.fail();
}
} // 测试乘法
public void testMultiply(){
System.out.println("测试方法testMultiply...");
Assert.assertEquals.(3, cal.multiply(1, 2));
} // 测试除法
public void testDivice(){
System.out.println("测试方法testDivide...");
Assert.assertEquals(2, cal.divide(1, 0));
} @Override
protected void tearDown() throws Exception{
super.tearDown();
cal = null; // 在每个测试方法执行后,主动销毁对象
System.out.println("在每个测试方法执行后执行--tearDown...\n");
} // main方法调用
public static void main(String[] args){
// 以文字输出的方式,显示运行结果
junit.textui.TestRunner.run(TestSample.class);
// 以图形化界面的方式,显示运行结果
// junit.swingui.TestRunner.run(TestSample.class);
// 以图形化界面的方式,显示运行结果
// junit.awtui.TestRunner.run(TestSample.class);
}
}

备注

  • Failures:表示测试的期待结果与程序运行结果不相符;
  • Errors: 表示测试程序执行过程中,抛出了异常;

2. JUnit运行流程(DEBUG模式运行)

  • JUnit的完整生命周期分为三个阶段:初始化阶段,运行阶段和结果捕捉阶段

2.1 初始化阶段(创建TestCase及TestSuite)

  • 图中红框处,采用了Composite Pattern;while循环第一次,获取TestSample中的方法,

    第二次循环,获取其父类TestCase中的方法: superClass = superClass.getSuperclass();;
// TestSuite.java
public class TestSuite implements Test{ private Vector fTests = new Vector(10);
private String fName; ...(略)
// 将符合条件的测试方法转化为TestCase,并存入到集合中
public void addTestMethod(Method m, Vector names, Class theClass){
String name = m.getName();
if(names.contains(name))
return; // 判断是否是公共方法
if(!isPublicTestMethod(m)){ // 判断是否是测试方法
if(isTestMethod(m))
addTest(warning("Test method isn't public: "+m.getName()));
return;
}
names.addElement(name); // 将testXXX方法转化为 TestCase
addTest(createTest(theClass, name));
} public void addTest(Test test){
fTests.addElement(test);
} // 判断是否是公共方法
public boolean isPublicTestMethod(Method m){
return isTestMethod(m) && Modifier.isPublic(m.getModifiers());
} // 判断是否是测试方法
// 1. 名称以"test"开头
// 2. 无参数,无返回值
public boolean isTestMethod(Method m){
String name = m.getName();
Class[] parameters = m.getParameterTypes();
Class returnType = m.getReturnType();
return parameters.length == 0 && name.startWith("test") && returnType.equals(Void.TYPE);
} // 根据testXXX名称,创建对应的TestCase
static public Test createTest(Class theClass, String name){
Constructor constructor;
try{
constructor = getTestConstructor(theClass);
}catch(NoSuchMethodException e){
...(略)
}
Object test;
try{
if(constructor.getParameterTypes().length == 0){
test = constructor.newInstance(new Object[0]);
if(test instanceof TestCase)
((TestCase)test).setName(name);
}else{
test = constructor.newInstance(new Object[]{name});
}
}catch(Exception e){
...(略)
}
return (Test)test;
}
}

2.2 运行阶段(运行所有TestXXX型的测试方法)

  • 创建TestResult实例;
  • junit.textui.TestRunner的监听器fPrinter加入到result的监听器列表中(观察者模式);
  • 开始计时;
  • run(result)测试运行;
  • 结束计时;
  • 结果输出;

参考资料:

Junit 3.8.1 源码分析(一)的更多相关文章

  1. Junit 3.8.1 源码分析之两个接口

    1. Junit源码文件说明 runner framework:整体框架; extensions:可以对程序进行扩展; textui:JUnit运行时的入口程序以及程序结果的呈现方式; awtui:J ...

  2. JUnit源码分析 - 扩展 - 自定义Rule

    JUnit Rule简述 Rule是JUnit 4.7之后新加入的特性,有点类似于拦截器,可以在测试类或测试方法执行前后添加额外的处理,本质上是对@BeforeClass, @AfterClass, ...

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

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

  4. spring源码分析(二)Aop

    创建日期:2016.08.19 修改日期:2016.08.20-2016.08.21 交流QQ:992591601 参考资料:<spring源码深度解析>.<spring技术内幕&g ...

  5. 【JUnit4.10源码分析】5 Statement

    假设要评选JUnit中最最重要的类型.或者说核心,无疑是org.junit.runners.model.Statement.Runner等类型看起来热闹而已. package org.junit.ru ...

  6. Robotium源码分析之Instrumentation进阶-attach

    在分析Robotium的运行原理之前,我们有必要先搞清楚Instrumentation的一些相关知识点,因为Robotium就是基于Instrumentation而开发出来的一套自动化测试框架.鉴于之 ...

  7. Robotium源码分析之Instrumentation进阶

    在分析Robotium的运行原理之前,我们有必要先搞清楚Instrumentation的一些相关知识点,因为Robotium就是基于Instrumentation而开发出来的一套自动化测试框架.鉴于之 ...

  8. Hadoop之HDFS原理及文件上传下载源码分析(上)

    HDFS原理 首先说明下,hadoop的各种搭建方式不再介绍,相信各位玩hadoop的同学随便都能搭出来. 楼主的环境: 操作系统:Ubuntu 15.10 hadoop版本:2.7.3 HA:否(随 ...

  9. MyBatis源码分析之环境准备篇

    前言 之前一段时间写了[Spring源码分析]系列的文章,感觉对Spring的原理及使用各方面都掌握了不少,趁热打铁,开始下一个系列的文章[MyBatis源码分析],在[MyBatis源码分析]文章的 ...

随机推荐

  1. spring-boot 集合mybatis 的分页查询

    spring-boot 集合mybatis 的github分页查询 一.依赖包 <!-- mysql 数据库驱动. --> <dependency> <groupId&g ...

  2. JSF request参数传递

    转载自:http://blog.csdn.net/duankaige/article/details/6711044 1:JSF页面之间传参 方法1: <h:outputLink value=& ...

  3. with as 和update ,Delete,insert

    这个SQL写了很久的时间,感觉pgSQL的很是麻烦. with as 先命名一个表出来,就可以当成临时表用. WITH tmp AS ( SELECT MAX(mgi.inner_cd) AS inn ...

  4. C语言 · 数组查找及替换

    算法训练 数组查找及替换   时间限制:1.0s   内存限制:512.0MB      问题描述 给定某整数数组和某一整数b.要求删除数组中可以被b整除的所有元素,同时将该数组各元素按从小到大排序. ...

  5. 搭建自己的GitHub Pages

    本文记录博主使用Win 10操作系统和Jekyll 3.1.2搭建GitHub Pages的过程.希望能帮助到相同有需要的朋友. 基本需求 GitHub账号及一个命名为{GitHub昵称}.githu ...

  6. elasticsearch -- 问题纪录

    报错1: [1]: max file descriptors [65535] for elasticsearch process is too low, increase to at least [6 ...

  7. 第二百四十八节,Bootstrap轮播插件

    Bootstrap轮播插件 学习要点: 1.轮播插件 本节课我们主要学习一下 Bootstrap 中的轮播插件. 一.轮播 轮播插件就是将几张同等大小的大图,按照顺序依次播放. 基本实例. 第一步,给 ...

  8. doAfterBody()方法是在( )接口中定义的。

    A.Tag B.IterationTag C.BodyTag D.TagSupport 解答:B

  9. 如果想要跨平台,在file类下有separtor(),返回锁出平台的文件分隔符

    对于命令:File f2=new file(“d:\\abc\\789\\1.txt”) 这个命令不具备跨平台性,因为不同的OS的文件系统很不相同. 如果想要跨平台,在file类下有separtor( ...

  10. 一个try可以跟进多个catch语句,用于处理不同情况,当一个try只能匹配一个catch

    一个try可以跟进多个catch语句,用于处理不同情况.当一个try只能匹配一个catch. 我们可以写多个catch语句,但是不能将父类型的exception的位置写在子类型的excepiton之前 ...