Junit4运行源码解析

intellij运行junit启动观察

首先我们在idea中运行一个最简单的Test,查看堆栈信息

在这里插入图片描述
在这里插入图片描述

总结:

  • 由上面堆栈可以看出运行启动类是JUnitStarter#main(args).
    不过是intellij中提供的,具体有兴趣的同学可以下去深究
    (com.intellij.junit4.JUnit4IdeaTestRunner)
  • . 从上图中可以看出,
    1. 首先通过Request#aClass创建一个ClassRequest
    2. 然后通过ClassRequest#getRunner 创建一个Runner进行测试

Runner入口

public abstract class Runner implements Describable {
    /*
     * (non-Javadoc)
     * @see org.junit.runner.Describable#getDescription()
     */
    public abstract Description getDescription();

    /**
     * Run the tests for this runner.
     *  (这个Runner就是用来运行tests的)
     *
     * @param notifier will be notified of events while tests are being run--tests being
     * started, finishing, and failing
     *  (参数notifier将在运行测试时收到事件通知. 包括test开始,结束和失败阶段,而notifier根据代码也可以看出拥有监听器listener )
     */
     
    public abstract void run(RunNotifier notifier);

    /**
     * @return the number of tests to be run by the receiver
     */
    public int testCount() {
        return getDescription().testCount();
    }
}

总结:

  • 可以得出Runner#run( org.junit.runner.Runner)就是用来运行test的入口。
    参数RunNotifier ,就是有各种监听器的notifier,估计大部分用不到,可以忽略

Runner的创建-org.junit.runner.Request

Junit中提供一个抽象的Request来创建Runner。设计模式之工厂方法模式

可以用 ctrl+H 查看下Request的类结构图,具体子类包括SortingRequest,RunnerRequest,FilterRequest,ClassRequest。

public abstract class Request {
   public abstract Runner getRunner();
}

在这里插入图片描述

  • . Request的子类都是用来创建Runner对象的,根据名字和具体代码可以得出,此处是设计模式之装饰模式。(动态增加和修改对象的行为,针对客户端是透明化的,可见的)

简单介绍一下,测试Junit的大致逻辑,就是利用最原始的ClassReuest创建Runner来执行测试,
然后就会获取@Test的方法,然后依次调用

RunnerRequest

  • 更简单了,就是直接构造传入Runner对象来创建

SortingRequest

  • 大致作用就是可以将@Test的方法进行排序。
    Runner对象需要实现org.junit.runner.manipulation.Sortable

FilterRequest

  • 大致作用就是可以将@Test的方法进行过滤,即表示是否运行,重写shouldRun方法。
    Runner对象需要实现org.junit.runner.manipulation.Filterable
  • 跟@Ignore差不多,只不过FilterRequest是编程式,@Ignore是注解式

因为junit是intellij发起入口测试的,所以上面的SortingRequest和FilterRequest其实不能手动自己去创建,所以可以暂时忽略,除非研究下怎么自己创建。或者扩展intellij的junit插件等。

ClassRequest

该类ClassRequest是基础类,就是根据我们的测试类Test,来创建Runner的
在这里插入图片描述

  • 值得一提的是,getRunner这里利用了一个DCL(双重检查锁)来创建Runner单例对象
    (注意 一定要用volatile来修饰,保证内存可见性和防止指令重排,如果看到没有使用volatile,肯定是错误的(除非你的cpu是单核的,也不太可能))

AllDefaultPossibilitiesBuilder

  • 此类利用了RunnerBuilder,有一个抽象方法#runnerForClass来创建Runner,
  • 根据代码来看,是利用了根据工厂方法模式来创建的对象(和Request#getRunner是相同的手法)。(从名字来看,好像是构建者模式,不要被误导了)
IgnoredBuilder
  • 如果测试类存在@Ignore(org.junit.Ignore),则可以从IgnoredClassRunner中发现,
    实际上最后就代表什么都不做do nothing,一个@Test都不会运行。
AnnotatedBuilder
  • 如果测试类存在@RunWith,则利用注解的class来反射实例化一个Runner对象。

    例如: @RunWith(SpringJUnit4ClassRunner.class) spring-test提供的Runner类
    在这里插入图片描述
SuiteMethodBuilder
  • 会去查找测试类Class,是否存在静态的static, 方法名为suite的,则直接当作测试Test执行. 如下
 public static junit.framework.Test suite(){
        return new junit.framework.Test(){
            @Override
            public int countTestCases() {
                return 0;
            }
            @Override
            public void run(TestResult result) {
                System.out.println("haha");
            }
        };
    }
JUnit3Builder
  • 测试类是TestCase的子类用来执行。 是属于junit3的一种方式。基本可以忽略了
public class TestDemo extends  junit.framework.TestCase{
  --重写来运行的方法
}
JUnit4Builder
  • 直接默认用BlockJUnit4ClassRunner来作为Runner运行测试。
    也是目前junit4版本的默认实现0
public class JUnit4Builder extends RunnerBuilder {
    @Override
    public Runner runnerForClass(Class<?> testClass) throws Throwable {
        return new BlockJUnit4ClassRunner(testClass);
    }
}

Statement

  • org.junit.runners.model.Statement, 当test测试类执行的时候,实际执行的就是该对象
  • 最底层是直接反射调用@Test方法,然后利用装饰模式,一层层的添加各种行为
    1. 例如前置@BeforeClass 或者 @Before
    2. 后置@AfterClass或者@After
    3. 异常处理,@Test 中的expected属性
    4. 超时Timeout处理,@Test 中的timeout属性,使用FutureTask
/**
 * Represents one or more actions to be taken at runtime in the course
 * of running a JUnit test suite.
 *
 * @since 4.5
 */
 public abstract class Statement {
    /**
     * Run the action, throwing a {@code Throwable} if anything goes wrong.
     */
    public abstract void evaluate() throws Throwable;
}

Test类运行的前置/后置

  • 定义在ParentRunner#classBlock中
    在这里插入图片描述

前置RunBefores实现

  1. 针对@BeforeClass
    在@Test方法执行之前,依次调用所有的@BeforeClass静态方法,必须是static
  2. 针对@Before
    会在@Test方法执行之前,依次调用所有的@Before实例方法

后置RunAfter

  1. 针对@AfterClass
    在@Test方法执行之后,依次调用所有的@AfterClass静态方法,必须是static
  2. 针对@After
    会在@Test方法执行之后,依次调用所有的@After实例方法

依次顺序

@BeforeClass > (创建test实例对象) >@Before > @Test > @After > @AfterClass

ParentRunner

  • 作为Runner的抽象父类,定义了run方法执行的逻辑(设计模式之模板模式)。
    提供getChildren和runChild两个抽象方法供子类自己实现
  • 并且使用Statement对象用来执行
 @Override
    public void run(final RunNotifier notifier) {
        EachTestNotifier testNotifier = new EachTestNotifier(notifier,
                getDescription());
        try {
            Statement statement = classBlock(notifier);
            statement.evaluate();
        } catch (AssumptionViolatedException e) {
            testNotifier.addFailedAssumption(e);
        } catch (StoppedByUserException e) {
            throw e;
        } catch (Throwable e) {
            testNotifier.addFailure(e);
        }
    }
    
   protected Statement classBlock(final RunNotifier notifier) {
        Statement statement = childrenInvoker(notifier);
        if (!areAllChildrenIgnored()) {
            statement = withBeforeClasses(statement);
            statement = withAfterClasses(statement);
            statement = withClassRules(statement);
        }
        return statement;
    }

BlockJUnit4ClassRunner

是ParentRunner运行类的子类,也是junit4版本的默认执行类,类结构图如下:
在这里插入图片描述

  • 下面是子类BlockJUnit4ClassRunner的相关代码:实现了父类的runChild和getChild
    具体逻辑就是获取测试类Test所有的@Test注解的方法,然后运行
    (当然会排除@Ignore(org.junit.Ignore)标识的方法)
   
   @Override
   protected void runChild(final FrameworkMethod method, RunNotifier notifier) {
       Description description = describeChild(method);
       if (isIgnored(method)) {
           notifier.fireTestIgnored(description);
       } else {
在 methodBlock是一个比较重要的下面开始讲解两个抽象方法供子类自己实现methodBlock重要的流程
           runLeaf(methodBlock(method), description, notifier);
       }
   } 
   
  @Override
   protected List<FrameworkMethod> getChildren() {
       return computeTestMethods();
   }

   
   // Override in subclasses
   
   /**
    * Returns the methods that run tests. Default implementation returns all
    * methods annotated with {@code @Test} on this class and superclasses that
    * are not overridden.
    */
   protected List<FrameworkMethod> computeTestMethods() {
       return getTestClass().getAnnotatedMethods(Test.class);
   }    

methodBlock方法

  • 就是用来创建最后执行的statement对象的,然后执行statement
    在这里插入图片描述
  1. 首先通过createTest()创建test实例对象
  2. 构造Statement对象,然后装饰为前置和后置的对象

最后附上流程图

在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值