JUnit5的启动

跟風遠走 提交于 2020-02-27 04:41:50

还发现有好帖: JUnit 5 简介,第 1 部分

第 2 部分

Launcher概述 Launcher是JUnit5的启动类,也是对启动进行扩展的主要入口,扩展通过实现自定义的TestEngine来自定义测试类的发现和执行逻辑以达到定制化测试的目的

Launcher启动示例代码

public static void main(String[] args) { //设置搜索和过滤规则 LauncherDiscoveryRequest request = LauncherDiscoveryRequestBuilder.request() .selectors( selectPackage("zj"), selectClass(*MyTest.class) ) .filters( includeClassNamePatterns(".*Tests") ) .build();

    Launcher launcher = LauncherFactory.create();
    // Register a listener of your choice
    //通过监听器来监听获取执行结果
    TestExecutionListener listener = new SummaryGeneratingListener();
    launcher.registerTestExecutionListeners(listener);
    launcher.execute(request);
}

启动分为如下几步:

  1. 构造LauncherDiscoveryRequest指定测试类的查找和过滤规则

2. 通过 LauncherFactory.create() 来获取Launcher默认实现

  1. 通过添加TestExecutionListener来进行测试结果的监听

  2. 执行launcher.execute(req)方法启动测试

简单来说就是扫描注定的文件夹或找到指定类名的类,经过Filter过滤后交给TestEngine去执行 启动过程中涉及的类 DiscoverySelector 负责定义哪些资源可以被TestEngine用来寻找测试类,比如指定java类名或者指定目录路径

Filter 作为TestEngine的过滤器,过滤DiscoverySelector发现的资源 LauncherDiscoveryRequest 使用不同的Filter定义了Launcher和TestEngine以及测试类的过滤规则,使用DiscoverySelector发现测试类 TestEngine 作为发现和执行符合LauncherDiscoveryRequest规则的测试的主要组件,对不同的编程模型可以实现自己的TestEngine,比如JupiterTestEngine

TestEngine在创建Launcher的时候通过JAVA SPI发现具体实现,JUnit5中有两种实现 :JupiterTestEngine 和 VintageTestEngine

TestExecutionListener 定义了测试执行期间发生的事件类型,注册到Launcher的实现类将会在事件发生时得到通知

DefaultLauncher 做为Launcher的默认实现,持有TestEngine和TestExecutionListener集合,它们通过JAVA SPI发现,负责执行TestEngine来启动测试

各步骤详解

  1. 构造LauncherDiscoveryRequest指定测试类的查找和过滤规则 public LauncherDiscoveryRequest build() { LauncherConfigurationParameters launcherConfigurationParameters = new LauncherConfigurationParameters( this.configurationParameters); return new DefaultDiscoveryRequest(this.selectors, this.engineFilters, this.discoveryFilters, this.postDiscoveryFilters, launcherConfigurationParameters); }

2.利用LauncherFactory创建DefaultLauncher public static Launcher create() throws PreconditionViolationException { Launcher launcher = new DefaultLauncher(new ServiceLoaderTestEngineRegistry().loadTestEngines()); for (TestExecutionListener listener : new ServiceLoaderTestExecutionListenerRegistry().loadListeners()) { launcher.registerTestExecutionListeners(listener); } return launcher; } 这里利用JAVA SPI来发现TestEngine和TestExecutionListener的实现集合,保存在Launcher中 3. 通过添加TestExecutionListener来进行测试结果的监听 TestExecutionListener listener = new SummaryGeneratingListener(); launcher.registerTestExecutionListeners(listener);

  1. 执行launcher.execute(req)方法启动测试 public void execute(LauncherDiscoveryRequest discoveryRequest, TestExecutionListener... listeners) { Preconditions.notNull(discoveryRequest, "LauncherDiscoveryRequest must not be null"); Preconditions.notNull(listeners, "TestExecutionListener array must not be null"); Preconditions.containsNoNullElements(listeners, "individual listeners must not be null"); execute(discoverRoot(discoveryRequest, "execution"), discoveryRequest.getConfigurationParameters(), listeners); } 4.1 调用discoveryRoot()来执行TestEngine的过滤器,过滤的结果通过类Root返回,Root持有TestEngine的map集合

    private Root discoverRoot(LauncherDiscoveryRequest discoveryRequest, String phase) { Root root = new Root();

     for (TestEngine testEngine : this.testEngines) {
     	// @formatter:off
     	boolean engineIsExcluded = discoveryRequest.getEngineFilters().stream()
     			.map(engineFilter -> engineFilter.apply(testEngine))
     			.anyMatch(FilterResult::excluded);
     	// @formatter:on
    
     	if (engineIsExcluded) {
     		logger.debug(() -> String.format(
     			"Test discovery for engine '%s' was skipped due to an EngineFilter in phase '%s'.",
     			testEngine.getId(), phase));
     		continue;
     	}
    
     	logger.debug(() -> String.format("Discovering tests during Launcher %s phase in engine '%s'.", phase,
     		testEngine.getId()));
    
     	Optional<TestDescriptor> engineRoot = discoverEngineRoot(testEngine, discoveryRequest);
     	engineRoot.ifPresent(rootDescriptor -> root.add(testEngine, rootDescriptor));
     }
     root.applyPostDiscoveryFilters(discoveryRequest);
     root.prune();
     return root;
    

    }

4.2 继续调用execute(Root ...)

private void execute(Root root, ConfigurationParameters configurationParameters,
		TestExecutionListener... listeners) {

	TestExecutionListenerRegistry listenerRegistry = buildListenerRegistryForExecution(listeners);
	TestPlan testPlan = TestPlan.from(root.getEngineDescriptors());
	TestExecutionListener testExecutionListener = listenerRegistry.getCompositeTestExecutionListener();
	testExecutionListener.testPlanExecutionStarted(testPlan);
	ExecutionListenerAdapter engineExecutionListener = new ExecutionListenerAdapter(testPlan,
		testExecutionListener);
	for (TestEngine testEngine : root.getTestEngines()) {
		TestDescriptor testDescriptor = root.getTestDescriptorFor(testEngine);
		execute(testEngine, new ExecutionRequest(testDescriptor, engineExecutionListener, configurationParameters));
	}
	testExecutionListener.testPlanExecutionFinished(testPlan);
}

主要逻辑就是构造监听器、TestDescriptor和ExecutionRequest的构造以及TestEngine的执行

Launcher构造和启动流程图

———————————————— 版权声明:本文为CSDN博主「Oo此岸花开oO」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/hqq2023623/article/details/78464877

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!