JUnit4执行cases背后的故事(1)---JUnitCore源码分析

(1)背景:

平常我们执行JUnit用例时,可以使用命令行或在IDE中使用Run As: JUnit Test,直接得到测试结果;但是背后的执行过程是怎么样的,值得我们深思,理解框架代码逻辑,有助于更好的使用该框架(或二次开发),本文将通过分析JUnitCore.java源码一一道来。

(2)org.junit.runner.JUnitCoreJUnitCore是什么?

JUnitCore使用外观模式(facade),对外提供一致的界面,同时支持运行JUnit 4或JUnit 3.8.x用例,通过命令行执行用例,形如:java org.junit.runner.JUnitCore TestClass1 TestClass2 ….

(3)实例:

Calculator.java 功能为计算形如“1+2+3+…”的和。

public class Calculator{
    // 计算累加和,形如“1+2+3”
    public int evaluate(String expression){
            int sum = 0;
            for(String summand:expression.split("\\+")){
                    sum += Integer.valueOf(summand);
            }
            return sum;

    }

}

CalculatorTest.java 为测试new Calculator().evaluate(String expression)的标准JUnit4测试类。

// 测试evaluate方法:    
import static org.junit.Assert.assertEquals;
import org.junit.Test;

public class CalculatorTest{
    @Test
    public void testEvaluatesExpression(){
            Calculator calculator = new Calculator();
            int sum = calculator.evaluate("1+2+3");
            assertEquals(6, sum);
    }
}

命令行执行JUnit:

[root@localhost junittest]# java -cp .:junit-4.10.jar org.junit.runner.JUnitCore CalculatorTest
JUnit version 4.10
.
Time: 0.01

OK (1 test)

输出结果解析:The single . means that one test has been run and the OK in the last line tells you that your test is successful.

JUnitCore.java源码

JUnitCore构造函数:

public JUnitCore() {
    this.notifier = new RunNotifier();
}

首先新建RunNotifier(运行通知器),告知JUnit,运行用例的进展。

JUnitCore入口主函数:

public static void main(String[] args) {
    Result result = new JUnitCore().runMain(new RealSystem(), args);
    System.exit((result.wasSuccessful()) ? 0 : 1);
}

主函数调用runMain(new RealSystem(), args)方法,返回运行结果Result。

Result runMain(JUnitSystem system, String[] args) {
    system.out().println("JUnit version " + Version.id());

    JUnitCommandLineParseResult jUnitCommandLineParseResult = JUnitCommandLineParseResult
            .parse(args);

    RunListener listener = new TextListener(system);
    addListener(listener);

    return run(jUnitCommandLineParseResult.createRequest(defaultComputer()));
}

JUnitCommandLineParseResult
.parse(args),解析main()参数,将参数(测试类:CalculatorTest)赋给JUnitCommandLineParseResult类成员:classes;

实例化RunListener(运行监听器),将测试类所有方法的运行过程事件注册给RunNotifier(运行通知器)。

jUnitCommandLineParseResult.createRequest(defaultComputer())返回一个Request(描述怎么去执行用例)

在createRequest()方法中,会新建AllDefaultPossibilitiesBuilder实例,AllDefaultPossibilitiesBuilder extends RunBuilder, RunBuilder运行构建者,为测试类构建测试运行器(runners)的一种策略.
AllDefaultPossibilitiesBuilder.java源码:

public class AllDefaultPossibilitiesBuilder extends RunnerBuilder {
private final boolean canUseSuiteMethod;

public AllDefaultPossibilitiesBuilder(boolean canUseSuiteMethod) {
    this.canUseSuiteMethod = canUseSuiteMethod;
}

public Runner runnerForClass(Class<?> testClass) throws Throwable {
    List builders = Arrays.asList(new RunnerBuilder[] { ignoredBuilder(),
            annotatedBuilder(), suiteMethodBuilder(), junit3Builder(),
            junit4Builder() });

    for (RunnerBuilder each : builders) {
        Runner runner = each.safeRunnerForClass(testClass);
        if (runner != null) {
            return runner;
        }
    }
    return null;
}

protected JUnit4Builder junit4Builder() {
    return new JUnit4Builder();
}

protected JUnit3Builder junit3Builder() {
    return new JUnit3Builder();
}

protected AnnotatedBuilder annotatedBuilder() {
    return new AnnotatedBuilder(this);
}

protected IgnoredBuilder ignoredBuilder() {
    return new IgnoredBuilder();
}

protected RunnerBuilder suiteMethodBuilder() {
    if (this.canUseSuiteMethod) {
        return new SuiteMethodBuilder();
    }
    return new NullBuilder();
    }
}

其中,如下源码根据测试类寻找特定的Runner, 使用责任链模式,只要测试类在对应的RunBuilder不返回空,该类的测试运行器就属于该RunRuilder下的runner。

for (RunnerBuilder each : builders) {
        Runner runner = each.safeRunnerForClass(testClass);
        if (runner != null) {
            return runner;
        }
    }

foreach依次判断测试类属于哪一个Runner: ignoredBuilder, annotatedBuilder, suiteMethodBuilder,junit3Builder , junit4Builder, 判断条件如下:

ignoredBuilder的判断条件:
testClass.getAnnotation(Ignore.class) != null

annotatedBuilder的判断条件:
RunWith annotation = (RunWith) currentTestClass
                .getAnnotation(RunWith.class);
if (annotation != null)
    return buildRunner(annotation.value(), testClass);

suiteMethodBuilder的判断条件:
if (this.canUseSuiteMethod) 

junit3Builder的判断条件:
if (isPre4Test(testClass)) 


junit4Builder的判断条件:
直接返回return new BlockJUnit4ClassRunner(testClass),就是说以上判断条件都不满足时,使用junit4Builder

所以,平时我们写JUnit4用例时,不添加任何@RunWith,默认使用BlockJUnit4ClassRunner测试运行器。

最后执行:

public Result run(Runner runner) {
    Result result = new Result();
    RunListener listener = result.createListener();
    this.notifier.addFirstListener(listener);
    try {
this.notifier.fireTestRunStarted(runner.getDescription());
        runner.run(this.notifier);
        this.notifier.fireTestRunFinished(result);
    } finally {
        removeListener(listener);
    }
    return result;
}

this.notifier.fireTestRunStarted(runner.getDescription());告知运行通知器notifier测试开始。
runner.run(this.notifier);使用对应的测试运行器执行用例,本文测试运行器为BlockJUnit4ClassRunner, statement.evaluate()执行用例。
this.notifier.fireTestRunFinished(result);获取执行结果result。

(4)JUnitCore源码时序图:

JUnit4执行cases背后的故事(1)---JUnitCore源码分析_第1张图片

附录:

org.junit.runner.JUnitCore

JUnitCore is a facade for running tests. It supports running JUnit 4 tests, JUnit 3.8.x tests, and mixtures. To run tests from the command line, run java org.junit.runner.JUnitCore TestClass1 TestClass2 ….

org.junit.runner.notification.RunListener

Register an instance of this class with RunNotifier to be notified of events that occur during a test run. All of the methods in this class are abstract and have no implementation; override one or more methods to receive events.

org.junit.runner.notification.RunNotifier

If you write custom runners, you may need to notify JUnit of your progress running tests. Do this by invoking the RunNotifier passed to your implementation of Runner.run(RunNotifier). Future evolution of this class is likely to move fireTestRunStarted(Description) and fireTestRunFinished(Result) to a separate class since they should only be called once per run.

org.junit.runners.model.RunnerBuilder

A RunnerBuilder is a strategy for constructing runners for classes. Only writers of custom runners should use RunnerBuilders. A custom runner class with a constructor taking a RunnerBuilder parameter will be passed the instance of RunnerBuilder used to build that runner itself.

org.junit.runner.Computer

Represents a strategy for computing runners and suites.

org.junit.runner.Request

A Request is an abstract description of tests to be run.

org.junit.runners.model.Statement

Represents one or more actions to be taken at runtime in the course of running a JUnit test suite.

你可能感兴趣的:(JUnit)