用 Junit5 测试的执行顺序由启动器/引擎控制。

huangapple go评论60阅读模式
英文:

order execution of Junit5 tests by launcher / engine

问题

我有一个Spring Boot 2.5.4项目,其中包含一些@SpringBootTest测试和一些@Cucumber测试。我使用gradle进行构建。

我注意到我的构建失败取决于它在哪里执行,我发现它实际上取决于测试执行的顺序,所以我的测试中有一个问题:如果@SpringBootTest首先运行,那么它通过。如果@Cucumber测试首先运行,那么它失败 - 可能是因为H2数据库在两者之间没有完全重置。

现在,我想临时控制执行顺序,以便我可以一致地复现问题,以修复我的测试之间的数据依赖关系。

我正在尝试使用junit.jupiter.testclass.order.default属性,其值为org.junit.jupiter.api.ClassOrderer$ClassName,但它不起作用。

我将我的两个测试放在了一个Junit5的@Suite中,在@SelectClasses中提到了这两个测试并改变了它们的顺序,但即使这样,它也不起作用 - 我的感觉是因为实际上有两个测试运行程序,Junit Jupiter和Cucumber。有时当我更改一些看似无关的东西时,执行顺序会改变:

用 Junit5 测试的执行顺序由启动器/引擎控制。

用 Junit5 测试的执行顺序由启动器/引擎控制。

我正在覆盖Junit版本到最新版本,希望这有所帮助(并且Junit5 Suite可用),但这并没有帮助:

ext['junit-jupiter.version']='5.9.2'

我使用的是Cucumber 6.11.0。

我的gradle测试任务简单地是:

test {
    useJUnitPlatform()
    finalizedBy(project.tasks.jacocoTestReport)
}

有没有一种方式可以在我的构建中配置测试运行程序的执行顺序,以一种一致且可重现的方式?

谢谢

英文:

I have a Spring Boot 2.5.4 project with some @SpringBootTest tests and some @Cucumber tests. I am using gradle to build.

I have noticed that my build is failing depending on where it's executed, and I found that it actually depended on the order the tests get executed, so I have a problem in my tests : if @SpringBootTest runs first then it's passing. if @Cucumber tests run first then it fails - probably because the H2 DB doesn't get fully reset in between.

Now, I would like to temporarily control the execution order, so that I can reproduce the issue consistently to fix the data dependency between my tests.

I am trying to use junit.jupiter.testclass.order.default property with value org.junit.jupiter.api.ClassOrderer$ClassName but it's not working.

I've put my 2 tests in a Juint5 @Suite mentioning the 2 tests in the @SelectClasses and changing their order, but even like that, it's not working - my feeling is that it's because there are actually 2 test runners, Junit Jupiter and Cucumber. Sometimes when I change something that doesn't seem related, the execution order changes :

用 Junit5 测试的执行顺序由启动器/引擎控制。

用 Junit5 测试的执行顺序由启动器/引擎控制。

I'm overriding Junit version to latest, hoping that it helps (and Junit5 Suite is available), but it doesn't help :

ext['junit-jupiter.version']='5.9.2'

I am using Cucumber 6.11.0.

My gradle test task is simply

test {
    useJUnitPlatform()
    finalizedBy(project.tasks.jacocoTestReport)
}

Is there a way to configure in my build the order in which the test runners get executed, in a consistent and reproduceable manner ?

Thanks

答案1

得分: 1

Gradle使用Junit Platform Launcher API来运行测试。你可以做同样的事情。为了确保顺序保持不变,你需要在同一个JVM内两次调用测试平台。

这应该允许你重现问题,因为Spring会保持应用程序运行,直到JVM退出。

import static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass;

import my.project.spring.MySpringBootTest;
import my.project.cucumber.MyCucumberTest;
import org.junit.platform.launcher.Launcher;
import org.junit.platform.launcher.LauncherDiscoveryRequest;
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.launcher.listeners.TestExecutionSummary;

public class ManualLaunch {

  public static void main(String[] args) {

    var cucumberSummary = runCucumberTest();
    var jupiterSummary = runJupiterTest();
    
    System.out.println("Jupiter failures : " + jupiterSummary.getTestsFailedCount());
    System.println("Cucumber failures : " + cucumberSummary.getTestsFailedCount());

  }

  private static TestExecutionSummary runCucumberTest() {

    LauncherDiscoveryRequest request = LauncherDiscoveryRequestBuilder.request()
        // 在这里配置Cucumber的请求
        .selectors(
            selectClass(MyCucumberTest.class)
        )
        .build();

    return launchAndGetSummary(request);
  }

  private static TestExecutionSummary runJupiterTest() {
    LauncherDiscoveryRequest request = LauncherDiscoveryRequestBuilder.request()
        .selectors(
            selectClass(MySpringBootTest.class)
        )
        .build();
      
    return launchAndGetSummary(request);
  }
    
  private static TestExecutionSummary launchAndGetSummary(LauncherDiscoveryRequest request) {
    Launcher launcher = LauncherFactory.create();
    SummaryGeneratingListener listener = new SummaryGeneratingListener();
    launcher.registerTestExecutionListeners(listener);
    launcher.execute(request);

    return listener.getSummary();
  }
}

如果你不知道如何从Gradle运行主方法,你也可以使用JUnit来运行JUnit。但是你需要确保测试不会以自身为目标。

英文:

Gradle uses the JUnit Platform Launcher API to run the tests. You can do the same thing. And to ensure the order remains the same, you would invoke the platform twice within the same JVM.

This should allow you to reproduce your problem as Spring keeps the application running until the JVM exits.

import static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass;

import my.project.spring.MySpringBootTest;
import my.project.cucumber.MyCucumberTest;
import org.junit.platform.launcher.Launcher;
import org.junit.platform.launcher.LauncherDiscoveryRequest;
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.launcher.listeners.TestExecutionSummary;

public class ManualLaunch {

  public static void main(String[] args) {

    var cucumberSummary= runCucumberTest();
    var jupiterSummary= runJupiterTest();
    
    System.out.println("Jupiter failures : "+jupiterSummary.getTestsFailedCount());
    System.out.println("Cucumber failures : "+cucumberSummary.getTestsFailedCount());

  }

  private static TestExecutionSummary runCucumberTest() {

    LauncherDiscoveryRequest request = LauncherDiscoveryRequestBuilder.request()
        // Configure the request for Cucumber here
        .selectors(
            selectClass(MyCucumberTest.class)
        )
        .build();

    return launchAndGetSummary(request);
  }

    private static TestExecutionSummary runJupiterTest() {
      LauncherDiscoveryRequest request = LauncherDiscoveryRequestBuilder.request()
          .selectors(
              selectClass(MySpringBootTest.class)
          )
          .build();
      
      return launchAndGetSummary(request);
    }
    
    private static TestExecutionSummary launchAndGetSummary(LauncherDiscoveryRequest request){
      Launcher launcher = LauncherFactory.create();
      SummaryGeneratingListener listener = new SummaryGeneratingListener();
      launcher.registerTestExecutionListeners(listener);
      launcher.execute(request);

      return listener.getSummary();
      
    }

}

If you don't know how to run a main method from Gradle, you could also use JUnit to run JUnit. But then you do have to make sure the tests don't target themselves.

huangapple
  • 本文由 发表于 2023年2月18日 03:36:18
  • 转载请务必保留本文链接:https://go.coder-hub.com/75488577.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定