英文:
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。有时当我更改一些看似无关的东西时,执行顺序会改变:
我正在覆盖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 :
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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论