暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

Junit5 源码分析系列(四)

测开技术笔记 2021-04-18
562

  前面讲了测试用例相关的数据结构是怎么生成的,当框架解析了测试用例后,后面就是执行了。执行方法的入口在DefaultLauncher.execute()方法

@Override
public void execute(LauncherDiscoveryRequest discoveryRequest, TestExecutionListener... listeners) {
......
execute(InternalTestPlan.from(discoverRoot(discoveryRequest, "execution")), listeners);
}


@Override
public void execute(TestPlan testPlan, TestExecutionListener... listeners) {
......
execute((InternalTestPlan) testPlan, listeners);
}

private void execute(InternalTestPlan internalTestPlan, TestExecutionListener[] listeners) {
Root root = internalTestPlan.getRoot();
ConfigurationParameters configurationParameters = root.getConfigurationParameters();
TestExecutionListenerRegistry listenerRegistry = buildListenerRegistryForExecution(listeners);
    //构造lambda表达式,遍历testEngine,分别执行其中的testDescriptor
withInterceptedStreams(configurationParameters, listenerRegistry, testExecutionListener -> {
      ......
ExecutionListenerAdapter engineExecutionListener = new ExecutionListenerAdapter(internalTestPlan,
testExecutionListener);
      //循环遍历testEngine 中的estDescriptor ,依次执行
for (TestEngine testEngine : root.getTestEngines()) {
TestDescriptor testDescriptor = root.getTestDescriptorFor(testEngine);
execute(testEngine,
          //构造ExecutionRequest对象
new ExecutionRequest(testDescriptor, engineExecutionListener, configurationParameters));
}
      ......
});
}

      实际依次调用执行了三个execute方法,最后execute执行的参数是构造的InternalTestPlan对象,使用InternalTestPlan.from()方法生成

static InternalTestPlan from(Root root) {
    //生成委托对象,传入前面解析完成的根节点TestDescriptor即JupiterEngineDescriptor
TestPlan delegate = TestPlan.from(root.getEngineDescriptors());
return new InternalTestPlan(root, delegate);
}


public static TestPlan from(Collection<TestDescriptor> engineDescriptors) {
Preconditions.notNull(engineDescriptors, "Cannot create TestPlan from a null collection of TestDescriptors");
TestPlan testPlan = new TestPlan(engineDescriptors.stream().anyMatch(TestDescriptor::containsTests));
    //添加到testPlan中的allIdentifiers map中
Visitor visitor = descriptor -> testPlan.add(TestIdentifier.from(descriptor));
engineDescriptors.forEach(engineDescriptor -> engineDescriptor.accept(visitor));
return testPlan;
}

       在withInterceptedStreams方法中,主要就是执行lambda表达式,所以重点还是execute(InternalTestPlan internalTestPlan, TestExecutionListener[] listeners)方法中的lambda表达式,如上述中循环遍历testEngine根节点,调用第四个execute方法 execute(TestEngine testEngine, ExecutionRequest executionRequest)执行,在其中调用testEngine的execute方法执行,入参ExecutionRequest 是一个包装类,主要包含TestDescriptor对象和EngineExecutionListener监听器对象

private void withInterceptedStreams(ConfigurationParameters configurationParameters,
TestExecutionListenerRegistry listenerRegistry, Consumer<TestExecutionListener> action) {
    //获取CompositeTestExecutionListener,实际是个组合类,listener的组合,
    //其中主要调用notifyEach 方法依次执行所有的listener对应的方法,如executionStarted,executionFinished等
TestExecutionListener testExecutionListener = listenerRegistry.getCompositeTestExecutionListener();
    ......
    try {
      //执行lambda表达式
action.accept(testExecutionListener);
}
finally {
       ......
}
}

  
private void execute(TestEngine testEngine, ExecutionRequest executionRequest) {
try {
//调用engine的excute方法
testEngine.execute(executionRequest);
}
catch (Throwable throwable) {
handleThrowable(testEngine, "execute", throwable);
}
}


      说到testEngine的execute方法,我们先看看JupiterTestEngine的继承结构,如下图所示:

      由于JupiterTestEngine中没有实现execute方法,会直接调用父类HierarchicalTestEngine中的execute方法

@Override
public final void execute(ExecutionRequest request) {
    //创建执行器service,如果没有并非执行相关配置,默认直接返回单线程执行器service
    //这里返回默认执行器service - SameThreadHierarchicalTestExecutorService
try (HierarchicalTestExecutorService executorService = createExecutorService(request)) {
      //创建<C extends EngineExecutionContext>,这里返回JupiterEngineExecutionContext对象
C executionContext = createExecutionContext(request);
      //返回包含 TestAbortedException(依赖于opentest4j库)  Predicate<? super Throwable> 的 OpenTest4JAwareThrowableCollector
ThrowableCollector.Factory throwableCollectorFactory = createThrowableCollectorFactory(request);
      //调用HierarchicalTestExecutor的execute方法,并get取得结果
new HierarchicalTestExecutor<>(request, executionContext, executorService,
throwableCollectorFactory).execute().get();
}
catch (Exception exception) {
throw new JUnitException("Error executing tests for engine " + getId(), exception);
}
}

  //HierarchicalTestExecutor 的execute方法
Future<Void> execute() {
TestDescriptor rootTestDescriptor = this.request.getRootTestDescriptor();
EngineExecutionListener executionListener = this.request.getEngineExecutionListener();
    //walk方法解决资源竞争的问题,当前不涉及
    NodeExecutionAdvisor executionAdvisor = new NodeTreeWalker().walk(rootTestDescriptor);
    //通过listener,executionService,throwableCollectorFactory,executionAdvisor构造taskContext
NodeTestTaskContext taskContext = new NodeTestTaskContext(executionListener, this.executorService,
this.throwableCollectorFactory, executionAdvisor);
    //通过taskContext和 根testDescriptor构造NodeTestTask对象,这个对象是后面执行测试用例的关键
NodeTestTask<C> rootTestTask = new NodeTestTask<>(taskContext, rootTestDescriptor);


rootTestTask.setParentContext(this.rootContext);
    //提交给executorService执行NodeTestTask
return this.executorService.submit(rootTestTask);
}

      NodeTestTask 是执行测试用例的关键,下节将详细讲解其执行流程。

      To be continued...



文章转载自测开技术笔记,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论