Junit概述

前言

写完程序后,总要以前我总喜欢写个main函数测试下。但随着时间推移和程序变大。main显的不那边方法。此时,Junit测试框架顺应而生。

应用场景

Junit的测试被多大框架所集成:

  1. idea开发工具
  2. maven test

它们通过组件的方式最终触发如下代码

//JunitTest即包含测试方法的类
Result result = JUnitCore.runClasses(JunitTest.class);  
//对于执行失败的情况打印失败信息
//idea把失信信息界面画给我们看
for (Failure failure : result.getFailures()) {
   System.out.println(failure.toString());
}

//测试类
public class JunitTest {

    @Test
    public void yoyo(){
        System.out.println("yoyo");
        assertTrue("eeee", false);
    }

}

核心组成分析

以下是Junit4的核心类

Statement

表示运代码段接口,可以执行的代码。

  1. InvokeMethod 其基本实现,表示如例子上面的@Test函数
  2. ExpectException @Test中expected异常处理,是否符合
  3. FailOnTimeout @Test中timeout超时处理
  4. RunBefores 表示方法执行前,@Before, BeforeClass
  5. RunAfters 表示方法执行后 @After,AfterClass
  6. RunRules表示方法的执行规则@Rule,ClassRule

通过以上方式并结合《设计模式—装饰模式》直接把代码串成一块。

Runner

测试用例的执行者。Statement的组织者。

  1. ParentRunner拆解单个测试类,并实现过滤基本功能
  2. Suite,ParentRunner实现之一,它可以包含多个类来进行测试执行。@SuiteClasses
  3. BlockJUnit4ClassRunner,ParentRunner实现之一。利用过滤功能实现@IncludeCategory,@ExcludeCategory,@Category,@Ignore
RunnerBuilder

用来给测试类创建runner,根据不同的测试类,可以创建不同的Runner,从而实现多版本支持。如BlockJUnit4ClassRunner,JUnit38ClassRunner支持。多功能支持如BlockJUnit4ClassRunnerWithParameters。spring Test也是靠这个实现的。

  1. IgnoredBuilder 支持@Ignore的Runner
  2. AnnotatedBuilder 支持@RunWith的
  3. SuiteMethodBuilder 简单的方法执行
  4. JUnit3Builder 支持Junit3版本
  5. junit4Builder 支持Junit4版本
  6. AllDefaultPossibilitiesBuilder 自动判断类,根据你编写类的不同风格,自动为选择Builder

通过以上方式并结合《设计模式—建造者模式》直接根据不同的Class定义创建不同的Runner

Request

根据对不同结果的期望,对请求进行区分。
1.ClassRequest 根据class转用Runner
2. FilterRequest 过滤掉一些Runner
3. SortingRequest 排序
通过以上方式并结合《设计模式—命令模式》,根据期望的不同结果,下不同的命令

Computer

建立Builder与真实Class联系。

执行流程分析

  1. 通过一切方式传入测试类
JUnitCoure.runClasses(Class<?>... classes){
   return runClasses(defaultComputer(), classes);
}
  1. 创建Request对象
public Result run(Computer computer, Class<?>... classes) {
    return run(Request.classes(computer, classes));
}

public static Request classes(Computer computer, Class<?>... classes) {
        try {
            //创建Builder策略
            AllDefaultPossibilitiesBuilder builder = new AllDefaultPossibilitiesBuilder(true);
            //computer使用之前AllDefaultPossibilitiesBuilder解析传进来Class转化为Runner
            //这里使用了一个代理Runner,延迟执行
            Runner suite = computer.getSuite(builder, classes);
            //使用最简单的Request策略
            return runner(suite);
        } catch (InitializationError e) {
            return runner(new ErrorReportingRunner(e, classes));
        }
    }

  1. 获取真正的Runner
//调用run(Runner runner)方法其中的runner参数是通过Request的getRunner()方法得来的。
//由2中的代码可知,这里得出的runner是从Computer#getSuite方法中得出的,该方法最终调用到的是传入的AllDefaultPossibilitiesBuilder,通过他的runnerForClass方法返回出真正的runner
    public Runner runnerForClass(Class<?> testClass) throws Throwable {
        //所有的Builde
        List<RunnerBuilder> builders = Arrays.asList(
                ignoredBuilder(),
                annotatedBuilder(),
                suiteMethodBuilder(),
                junit3Builder(),
                junit4Builder());
        //循环解析Class,然后判断其适合哪个Builder
        //junit4Builder为我们创造BlockJUnit4ClassRunner
        for (RunnerBuilder each : builders) {
            Runner runner = each.safeRunnerForClass(testClass);
            if (runner != null) {
                return runner;
            }
        }
        return null;
    }
  1. 方法真正被run的入口
 public Result run(Runner runner) {
        //结果类
        Result result = new Result();       
        RunListener listener = result.createListener();
        //notifier是监听者,加入监听器,绑定结果集
        notifier.addFirstListener(listener);
        try {
            notifier.fireTestRunStarted(runner.getDescription());
            //执行Runner
            runner.run(notifier);
            notifier.fireTestRunFinished(result);
        } finally {
            removeListener(listener);
        }
        return result;
    }

 public void run(final RunNotifier notifier) {
        EachTestNotifier testNotifier = new EachTestNotifier(notifier,
                getDescription());
        testNotifier.fireTestSuiteStarted();
        try {
            //1.如果是Suite Runner则会循环调用下面的所有Runner
            //2.加载Class等级的方法,
            Statement statement = classBlock(notifier);
            //先调用本类的Class等级的方法,再执行子Runner
            statement.evaluate(); 
        } catch (AssumptionViolatedException e) {
            testNotifier.addFailedAssumption(e);
        } catch (StoppedByUserException e) {
            throw e;
        } catch (Throwable e) {
            testNotifier.addFailure(e);
        } finally {
            testNotifier.fireTestSuiteFinished();
        }
    } 


  1. 获取所有需要被执行的测试方法
protected Statement classBlock(final RunNotifier notifier) {        
        //子集合Runner执行代码
        Statement statement = childrenInvoker(notifier);
        if (!areAllChildrenIgnored()) {
            statement = withBeforeClasses(statement);
            statement = withAfterClasses(statement);
            statement = withClassRules(statement);
            statement = withInterruptIsolation(statement);
        }
        return statement;
} 
      
  1. 方法最终被执行
 @Override
 //当Runner是BlockJUnit4ClassRunner
 //其子执行体如下
 protected void runChild(final FrameworkMethod method, RunNotifier notifier) {
        Description description = describeChild(method);
        if (isIgnored(method)) {
            notifier.fireTestIgnored(description);
        } else {
            Statement statement;
            try {
                //拼装@Before,@After之类的注解
                statement = methodBlock(method);
            }
            catch (Throwable ex) {
                statement = new Fail(ex);
            }
            runLeaf(statement, description, notifier);
        }
 }

主要参考

从执行流程分析Junit4源码
官方文档
Junit4框架分析报告
JUnit4学习笔记(四):利用Rule扩展JUnit

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值