Junit 4原理分析

Junit4 的入口(version: 4.12)

单元测试的入口是: JUnitCore类的main方法

启动测试的java命令行:
java.org.junit.runner.JUnitCore TestClass1 TestClass2 …

TestClass1, TestClass2是单元测试的类,它们会传到main(String[] args)的args参数里

分析一下运行流程:

JUnitCore的main方法就:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
 /**
* Run the tests contained in the classes named in the <code>args</code>.
* If all tests run successfully, exit with a status of 0. Otherwise exit with a status of 1.
* Write feedback while tests are running and write
* stack traces for all failed tests after the tests all complete.
*
* @param args names of classes in which to find tests to run
*
*
* 如果所有测试都成功,System.exit(0), 否则System.exit(1). 非0退出表示异常退出
*/
public static void main(String... args) {
Result result = new JUnitCore().runMain(new RealSystem(), args);
System.exit(result.wasSuccessful() ? 0 : 1);
}
runMain方法:
1
2
3
4
5
6
7
8
9
10
11
12
Result runMain(JUnitSystem system, String... args) {
system.out().println("JUnit version " + Version.id());

JUnitCommandLineParseResult jUnitCommandLineParseResult = JUnitCommandLineParseResult.parse(args);
//JUnitCommandLineParseResult存储所有class的单元测试

RunListener listener = new TextListener(system); //TextListener用于打印测试过程中的信息,system可以定义不同打印系统
addListener(listener);
//添加单元测试运行监听状态,(例如:开始,完成等)

return run(jUnitCommandLineParseResult.createRequest(defaultComputer())); //createRequest静态方法创建单元测试的Request
}
run方法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
	public Result run(Runner runner) {  // runner = request.getRunner();
Result result = new Result();
RunListener listener = result.createListener();
notifier.addFirstListener(listener);
try {
notifier.fireTestRunStarted(runner.getDescription());//notifier是JUnitCore一个成员变量,用于通知单元测试运行过程
runner.run(notifier); // 运行测试
notifier.fireTestRunFinished(result);
} finally {
removeListener(listener);
}
return result;
}

runner.run(notifier) 的runner是Suite类,Using Suite as a runner allows you to manually build a suite containing tests from many classes.
Suite封装一个可以手动构建测试,提供RunnerBuilder来构建。

默认情况下是AllDefaultPossibilitiesBuilder类:
@Override
public Runner runnerForClass(Class<?> testClass) throws Throwable {
List<RunnerBuilder> builders = Arrays.asList(
ignoredBuilder(), //定义忽略测试的规则
annotatedBuilder(), // 提供注解RunWith来扩展Runner
suiteMethodBuilder(),
junit3Builder(), // 兼容junit3
junit4Builder()); //常用的junit4

for (RunnerBuilder each : builders) {
Runner runner = each.safeRunnerForClass(testClass);
if (runner != null) {
return runner;
}
}
return null;
}
关键类PerentRunner:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/**
* Provides most of the functionality specific to a Runner that implements a
* "parent node" in the test tree, with children defined by objects of some data
* type {@code T}. (For {@link BlockJUnit4ClassRunner}, {@code T} is
* {@link Method} . For {@link Suite}, {@code T} is {@link Class}.) Subclasses
* must implement finding the children of the node, describing each child, and
* running each child. ParentRunner will filter and sort children, handle
* {@code @BeforeClass} and {@code @AfterClass} methods,
* handle annotated {@link ClassRule}s, create a composite
* {@link Description}, and run children sequentially.
*
*
*
* 一个抽象类,设计了测试树结构,提供了一个含有一系列功能的Runner,子类实现需要定义一个泛型的数据类型。
*/

PerentRunner类的抽象方法:

1
2
3
4
5
6
7
8
9
protected abstract List<T> getChildren();

protected abstract Description describeChild(T child);

protected abstract void runChild(T child, RunNotifier notifier);

private final TestClass testClass;
//Wraps a class to be run, providing method validation and annotation searching
//包装了将要运行单元测试的类,提供可测试方法验证,以及注解的查找.

run方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
 @Override
public void run(final RunNotifier notifier) {
EachTestNotifier testNotifier = new EachTestNotifier(notifier,
getDescription());
testNotifier.fireTestSuiteStarted();
try {
Statement statement = classBlock(notifier);
statement.evaluate();
} catch (AssumptionViolatedException e) {
testNotifier.addFailedAssumption(e);
} catch (StoppedByUserException e) {
throw e;
} catch (Throwable e) {
testNotifier.addFailure(e);
} finally {
testNotifier.fireTestSuiteFinished();
}
}

//Statement: Represents one or more actions to be taken at runtime in the course of running a JUnit test suite.
//代表一个或者多个action,这些action是一个单元测试套件在运行时需要执行的。
protected Statement classBlock(final RunNotifier notifier) {
Statement statement = childrenInvoker(notifier);
if (!areAllChildrenIgnored()) {
statement = withBeforeClasses(statement); // 在运行单元测试前执行,(提供注解BeforeClass)
statement = withAfterClasses(statement); // 在运行单元测试后执行,(提供注解AftersClass)
statement = withClassRules(statement); // 提供注解ClassRule拦截单元测试 the {@code ClassRule}s that can transform the block that runs each method in the tested class.
}
return statement;
}

执行后statement.evaluate()会执行:
private void runChildren(final RunNotifier notifier) {
final RunnerScheduler currentScheduler = scheduler;
try {
for (final T each : getFilteredChildren()) { // 循环由子类实现的List<T> getChildren()
currentScheduler.schedule(new Runnable() {
public void run() {
ParentRunner.this.runChild(each, notifier); //循环由子类实现的runChild(T child, RunNotifier notifier)
}
});
}
} finally {
currentScheduler.finished();
}
}

现在常用junit4, 关键类就是BlockJUnit4ClassRunner

1
2
3
4
5
6
7
8
9
10
@Override
protected List<FrameworkMethod> getChildren() {
return computeTestMethods();
}

protected List<FrameworkMethod> computeTestMethods() {
return getTestClass().getAnnotatedMethods(Test.class);
}

从上面代码可以知道,含有注解Test才是可以测试的方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
   @Override
protected void runChild(final FrameworkMethod method, RunNotifier notifier) {
Description description = describeChild(method);
if (isIgnored(method)) {
notifier.fireTestIgnored(description);
} else {
Statement statement;
try {
statement = methodBlock(method);//生产statement
}
catch (Throwable ex) {
statement = new Fail(ex);
}
runLeaf(statement, description, notifier); //执行test
}
}

protected Statement methodBlock(final FrameworkMethod method) {
Object test;
try {
test = new ReflectiveCallable() {
@Override
protected Object runReflectiveCall() throws Throwable {
return createTest(method);
}
}.run();
} catch (Throwable e) {
return new Fail(e);
}

Statement statement = methodInvoker(method, test);
statement = possiblyExpectingExceptions(method, test, statement);
statement = withPotentialTimeout(method, test, statement);
statement = withBefores(method, test, statement); //定义Before注解
statement = withAfters(method, test, statement); //定义After注解
statement = withRules(method, test, statement); //定义Rule注解
return statement;
}
Loading comments box needs to over the wall