自动化测试工具 junit4 执行 cases 背后的故事 (2)---默认执行器 blockjunit4classrunner 源码分析

胡刚 · October 07, 2015 · Last by 胡刚 replied at October 08, 2015 · 1140 hits

(1)背景:

之前《JUnit4执行cases背后的故事(1)---JUnitCore源码分析》介绍了JUnit4执行测试用例默认的执行器是BlockJUnit4ClassRunner,它内部是怎么实现的呢? 本文将一一介绍。

(2)org.junit.runners.BlockJUnit4ClassRunner是什么?

官方解释如下:

it is now the default test class runner, but it should have exactly the same behavior as the old test class runner (JUnit4ClassRunner).

BlockJUnit4ClassRunner has advantages for writers of custom JUnit runners that are slight changes to the default behavior, however:

It has a much simpler implementation based on Statements, allowing new operations to be inserted into the appropriate point in the execution flow.
It is published, and extension and reuse are encouraged, whereas JUnit4ClassRunner was in an internal package, and is now deprecated.

概括来说:

BlockJUnit4ClassRunner是默认的测试类执行器,可以通过它方便地实现自定义执行器(可以在执行流中插入新的操作),它执行测试的方式是基于Statement block,Statement分成数种职责(BeforeClass, AfterClass,Before,After,执行测试方法等),不同的Statement负责自己的职责,执行结束后交给下一个Statement,直到所有Statement执行完毕;设计模式是职责链模式(Chain Of Responsibility Pattern)。

类继承关系:

java.lang.Object
extended by org.junit.runner.Runner
extended by org.junit.runners.ParentRunner<FrameworkMethod>
extended by org.junit.runners.BlockJUnit4ClassRunner

(3)源码分析

执行用例时,会执行runner.run(this.notifier);(见JUnit4执行cases背后的故事(1)---JUnitCore源码分析 第三部分),该runner为BlockJUnit4ClassRunner实例,调用其父类ParentRunner方法:

@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);
}
}

run()方法调用classBlock(notifier)新建Statement实例(类Statement block)。

protected Statement classBlock(final RunNotifier notifier) {
Statement statement = childrenInvoker(notifier);
if (!areAllChildrenIgnored()) {
statement = withBeforeClasses(statement);
statement = withAfterClasses(statement);
statement = withClassRules(statement);
}
return statement;
}

classBlocke()方法调用childrenInvoker()新建Statement实例(方法Statement block)

protected Statement childrenInvoker(final RunNotifier notifier) {
return new Statement() {
@Override
public void evaluate() {
runChildren(notifier);
}
};
}

childrenInvoker()新建Statement实例,该实例重写evaluate()方法,runChildren()定义测试方法将要执行的调度策略。

private void runChildren(final RunNotifier notifier) {
final RunnerScheduler currentScheduler = scheduler;
try {
for (final T each : getFilteredChildren()) {
currentScheduler.schedule(new Runnable() {
public void run() {
ParentRunner.this.runChild(each, notifier);
}
});
}
} finally {
currentScheduler.finished();
}
}

getFilteredCildren()返回List<FrameworkMethod>, 每个FrameworkMethod实例是@Test标记的测试方法。
getFilteredCildren()方法内部通过getChildren(),computeTestMethods()获取到List。

protected List<FrameworkMethod> getChildren() {
return computeTestMethods();
}


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

runChildren()遍历List<FrameworkMethod>, 对每个FrameworkMethod调用BlockJUnit4ClassRunner的runChild()作为RunnerScheduler currentScheduler实例(RunnerScheduler:Represents a strategy for scheduling when individual test methods should be run (in serial or parallel))的执行体:

@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);
}
catch (Throwable ex) {
statement = new Fail(ex);
}
runLeaf(statement, description, notifier);
}
}

如果该方法未被标记@Ignore,则获取到该方法的Statement:

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);
statement = withAfters(method, test, statement);
statement = withRules(method, test, statement);
return statement;
}

methodInvoker()方法会新建InvokeMethod实例(Statement子类),InvokeMethod类重写evaluate()方法。

protected Statement methodInvoker(FrameworkMethod method, Object test) {
return new InvokeMethod(method, test);
}

public class InvokeMethod extends Statement {
private final FrameworkMethod testMethod;
private final Object target;

public InvokeMethod(FrameworkMethod testMethod, Object target) {
this.testMethod = testMethod;
this.target = target;
}

@Override
public void evaluate() throws Throwable {
testMethod.invokeExplosively(target);
}
}

evaluate()执行体testMethod.invokeExplosively(target), target值为test,invokeExplosively()方法返回@Test测试方法的执行结果。

/**
* Returns the result of invoking this method on {@code target} with
* parameters {@code params}. {@link InvocationTargetException}s thrown are
* unwrapped, and their causes rethrown.
*/

public Object invokeExplosively(final Object target, final Object... params)
throws Throwable {
return new ReflectiveCallable() {
@Override
protected Object runReflectiveCall() throws Throwable {
return method.invoke(target, params);
}
}.run();
}

runChild()调用父类ParentRunner的runLeaf()方法(方法Statement block部分)并执行该Statement block(通过statement.evaluate());

/**
* Runs a {@link Statement} that represents a leaf (aka atomic) test.
*/

protected final void runLeaf(Statement statement, Description description,
RunNotifier notifier) {
EachTestNotifier eachNotifier = new EachTestNotifier(notifier, description);
eachNotifier.fireTestStarted();
try {
statement.evaluate();
} catch (AssumptionViolatedException e) {
eachNotifier.addFailedAssumption(e);
} catch (Throwable e) {
eachNotifier.addFailure(e);
} finally {
eachNotifier.fireTestFinished();
}
}

通过Statement statement = childrenInvoker(notifier)获取method Statem block后,再获取class Statement block。

statement = withBeforeClasses(statement);
statement = withAfterClasses(statement);
statement = withClassRules(statement);

最后调用:statement.evaluate();依次执行evaluate()...

@Override
public void run(final RunNotifier notifier) {
EachTestNotifier testNotifier = new EachTestNotifier(notifier,
getDescription());
try {
Statement statement = classBlock(notifier);
statement.evaluate();

(4)总结

1.如果想在class Statement block加入自定义动作,可以修改classBlock()方法。
2.如果想在method Statement block加入自定义动作,可以修改methodBlock()方法。
3.如果想在@Test方法加入自定义动作,可以修改methodInvoker()方法。

所有修改必须定义一个Statement子类,并重写evaluate()方法定义自己的职责。

如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
共收到 2 条回复 时间 点赞

语法用 java 高亮吧

需要 Sign In 后方可回复, 如果你还没有账号请点击这里 Sign Up