如何使用 junit5 平台启动器 api 从队列中发现测试?

How do I use the junit5 platform launcher api to discover tests from a queue?

我希望将测试分发到 junit5 独立控制台的多个实例,每个实例都从队列中读取。 runner 的每个实例都会在类路径上使用相同的 test.jar,所以我不会在这里分发实际测试的字节代码,只是测试/过滤器模式字符串的名称。

根据 junit 5 advanced topics doc,我认为扩展 junit 5 的适当位置是使用平台启动器 api。我将这段代码主要与指南中的示例代码拼凑在一起。我认为这是我需要写的,但我担心我过度简化了这里涉及的工作:

// keep pulling test classes off the queue until its empty
while(myTestQueue.isNotEmpty()) {
    String classFromQueue = myTestQueue.next(); //returns "org.myorg.foo.fooTests"
    LauncherDiscoveryRequest request = LauncherDiscoveryRequestBuilder.request()
        .selectors(selectClass(classFromQueue)).build();
        
    SummaryGeneratingListener listener = new SummaryGeneratingListener();
    try (LauncherSession session = LauncherFactory.openSession()) {
        Launcher launcher = session.getLauncher();
        launcher.registerTestExecutionListeners(listener);
        TestPlan testPlan = launcher.discover(request);
        launcher.execute(testPlan);
    }   
    TestExecutionSummary summary = listener.getSummary();
    addSummary(summary);
}

问题:

  1. 在while循环中重复发现和执行是否会违反正常的测试生命周期?我有点不清楚发现是否是一次性的事情,应该在所有处决之前发生。
  2. 如果我假设可以重复发现然后执行,我发现 HierarchicalTestEngine 可能是从队列中读取的更好的地方,因为它似乎用于实现并行执行。这更适合我的用例吗?除了我可能不需要处理累积的测试摘要外,该实现是否与我上面的实现基本相同?

我不想采用的方法: 我不打算使用旨在在同一 jvm 中并行化测试执行的 junit 5 的新功能。我也不打算提前划分测试或 类;使用预先确定的测试子集启动每个控制台运行器实例。

简答

问题中发布的代码(松散地)说明了一种有效的方法。无需创建自定义引擎。利用平台启动器 api 重复发现和执行测试确实有效。我认为值得强调的是您不必扩展 junit5 这不是通过您需要注册的扩展来执行的,就像我最初假设的那样。您只是简单地利用平台启动器 api 来发现和执行测试。

长答案

这里是一些示例代码,其中包含存在于 class 路径上的简单测试队列 class 名称。当队列不为空时,testNode class 的实例将发现并执行三个测试中的每一个 classes 并编写 LegacyXmlReport。

测试节点代码:

package org.sample.node;
import org.junit.platform.launcher.Launcher;
import org.junit.platform.launcher.LauncherDiscoveryRequest;
import org.junit.platform.launcher.LauncherSession;
import org.junit.platform.launcher.TestPlan;
import org.junit.platform.launcher.core.LauncherConfig;
import org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder;
import org.junit.platform.launcher.core.LauncherFactory;
import org.junit.platform.launcher.listeners.SummaryGeneratingListener;
import org.junit.platform.reporting.legacy.xml.LegacyXmlReportGeneratingListener;

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.PrintWriter;
import java.nio.file.Paths;
import java.util.LinkedList;
import java.util.Queue;

import static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass;
public class TestNode {

    public void run() throws FileNotFoundException {

        // keep pulling test classes off the queue until its empty
        Queue<String> queue = getQueue();
        while(!queue.isEmpty()) {
            String testClass = queue.poll();
            LauncherDiscoveryRequest request = LauncherDiscoveryRequestBuilder.request()
                    .selectors(selectClass(testClass)).build();


            LauncherConfig launcherConfig = LauncherConfig.builder()
                    .addTestExecutionListeners(new LegacyXmlReportGeneratingListener(Paths.get("target"), new PrintWriter(new FileOutputStream("log.txt"))))
                    .build();

            SummaryGeneratingListener listener = new SummaryGeneratingListener();
            try (LauncherSession session = LauncherFactory.openSession(launcherConfig)) {
                Launcher launcher = session.getLauncher();
                launcher.registerTestExecutionListeners(listener);
                TestPlan testPlan = launcher.discover(request);
                launcher.execute(testPlan);
            }
        }
    }

    private Queue<String> getQueue(){
        Queue<String> queue = new LinkedList<>();
        queue.add("org.sample.tests.Class1");
        queue.add("org.sample.tests.Class2");
        queue.add("org.sample.tests.Class3");
        return queue;
    }

    public static void main(String[] args) throws FileNotFoundException {
        TestNode node = new TestNode();
        node.run();
    }
}

TestNode 执行的测试

我只展示三个测试 class 中的一个,因为它们都是相同的东西,只是 class 名称不同。 它们位于 src/main/java 而不是 src/test/java。这是 maven 中用于将测试打包到 fat jar 中的一种公认的奇怪但常见的模式。

package org.sample.tests;

import org.junit.jupiter.api.Test;

public class Class1 {
    @Test
    void test1() {
        System.out.println("Class1 Test 1");
    }

    @Test
    void test2() {
        System.out.println("Class1 Test 2");
    }

    @Test
    void test3() {
        System.out.println("Class1 Test 3");
    }
}