Open dunja132015 opened 1 month ago
I'm having some trouble reproducing your problem with the information provided.
Do you have a more precise minimal reproducer? You could fork the cucumber-java-skeleton and use that as a base.
Could you post the contents of the dumpstream
file that was created?
I updated the body:
Execute the test from command line (make sure you have surefire.rerunFailingTestsCount set):
>mvn test -Dsurefire.rerunFailingTestsCount=2 -Dcucumber.filter.tags="@XYZ"
Let me know if you can reproduce it with this information.
Unfortunately not. Could you make a minimal reproducer of the cucumber-java-skeleton?
The line numbers in the stack trace in the dump stream don't match the sources of maven-surefire-plugin version:3.3.1
so you may have another problem.
Hi @mpkorstanje, I just would like to mention I'm also facing this same issue. My bom version is
<dependency>
<groupId>io.cucumber</groupId>
<artifactId>cucumber-spring</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.cucumber</groupId>
<artifactId>cucumber-junit-platform-engine</artifactId>
<scope>test</scope>
</dependency>
for <maven-surefire-plugin.version>3.1.0</maven-surefire-plugin.version>
and <maven-compiler-plugin.version>3.8.1</maven-compiler-plugin.version>
. I'm trying as much as I can to try to find a reproducible issue for this but so far what I've noticed is that it seems to only happen for errors, not assertion failures.
This is my whole dump file:
# Created at 2024-08-28T16:25:32.681
ForkStarter IOException: Element name cannot be empty
Element name cannot be empty.
org.apache.maven.plugin.surefire.booterclient.output.MultipleFailureException: Element name cannot be empty
Element name cannot be empty
at org.apache.maven.plugin.surefire.booterclient.output.ThreadedStreamConsumer$Pumper.<init>(ThreadedStreamConsumer.java:59)
at org.apache.maven.plugin.surefire.booterclient.output.ThreadedStreamConsumer.<init>(ThreadedStreamConsumer.java:107)
at org.apache.maven.plugin.surefire.booterclient.ForkStarter.fork(ForkStarter.java:546)
at org.apache.maven.plugin.surefire.booterclient.ForkStarter.run(ForkStarter.java:285)
at org.apache.maven.plugin.surefire.booterclient.ForkStarter.run(ForkStarter.java:250)
at org.apache.maven.plugin.surefire.AbstractSurefireMojo.executeProvider(AbstractSurefireMojo.java:1203)
at org.apache.maven.plugin.surefire.AbstractSurefireMojo.executeAfterPreconditionsChecked(AbstractSurefireMojo.java:1055)
at org.apache.maven.plugin.surefire.AbstractSurefireMojo.execute(AbstractSurefireMojo.java:871)
at org.apache.maven.plugin.DefaultBuildPluginManager.executeMojo(DefaultBuildPluginManager.java:137)
at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:210)
at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:156)
at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:148)
at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject(LifecycleModuleBuilder.java:117)
at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject(LifecycleModuleBuilder.java:81)
at org.apache.maven.lifecycle.internal.builder.singlethreaded.SingleThreadedBuilder.build(SingleThreadedBuilder.java:56)
at org.apache.maven.lifecycle.internal.LifecycleStarter.execute(LifecycleStarter.java:128)
at org.apache.maven.DefaultMaven.doExecute(DefaultMaven.java:305)
at org.apache.maven.DefaultMaven.doExecute(DefaultMaven.java:192)
at org.apache.maven.DefaultMaven.execute(DefaultMaven.java:105)
at org.apache.maven.cli.MavenCli.execute(MavenCli.java:957)
at org.apache.maven.cli.MavenCli.doMain(MavenCli.java:289)
at org.apache.maven.cli.MavenCli.main(MavenCli.java:193)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:568)
at org.codehaus.plexus.classworlds.launcher.Launcher.launchEnhanced(Launcher.java:282)
at org.codehaus.plexus.classworlds.launcher.Launcher.launch(Launcher.java:225)
at org.codehaus.plexus.classworlds.launcher.Launcher.mainWithExitCode(Launcher.java:406)
at org.codehaus.plexus.classworlds.launcher.Launcher.main(Launcher.java:347)
Suppressed: java.lang.IllegalArgumentException: Element name cannot be empty
at org.apache.maven.surefire.shared.utils.xml.PrettyPrintXMLWriter.startElement(PrettyPrintXMLWriter.java:245)
at org.apache.maven.plugin.surefire.report.StatelessXmlReporter.getTestProblems(StatelessXmlReporter.java:430)
at org.apache.maven.plugin.surefire.report.StatelessXmlReporter.serializeTestClassWithRerun(StatelessXmlReporter.java:294)
at org.apache.maven.plugin.surefire.report.StatelessXmlReporter.serializeTestClass(StatelessXmlReporter.java:204)
at org.apache.maven.plugin.surefire.report.StatelessXmlReporter.testSetCompleted(StatelessXmlReporter.java:158)
at org.apache.maven.plugin.surefire.report.StatelessXmlReporter.testSetCompleted(StatelessXmlReporter.java:51)
at org.apache.maven.plugin.surefire.report.TestSetRunListener.testSetCompleted(TestSetRunListener.java:193)
at org.apache.maven.plugin.surefire.booterclient.output.ForkClient$TestSetCompletedListener.handle(ForkClient.java:143)
at org.apache.maven.plugin.surefire.booterclient.output.ForkClient$TestSetCompletedListener.handle(ForkClient.java:127)
at org.apache.maven.plugin.surefire.booterclient.output.ForkedProcessEventNotifier.notifyEvent(ForkedProcessEventNotifier.java:197)
at org.apache.maven.plugin.surefire.booterclient.output.ForkClient.handleEvent(ForkClient.java:303)
at org.apache.maven.plugin.surefire.booterclient.output.ForkClient.handleEvent(ForkClient.java:59)
at org.apache.maven.plugin.surefire.booterclient.output.ThreadedStreamConsumer$Pumper.run(ThreadedStreamConsumer.java:86)
at java.base/java.lang.Thread.run(Thread.java:833)
Suppressed: java.lang.IllegalArgumentException: Element name cannot be empty
at org.apache.maven.surefire.shared.utils.xml.PrettyPrintXMLWriter.startElement(PrettyPrintXMLWriter.java:245)
at org.apache.maven.plugin.surefire.report.StatelessXmlReporter.getTestProblems(StatelessXmlReporter.java:430)
at org.apache.maven.plugin.surefire.report.StatelessXmlReporter.serializeTestClassWithRerun(StatelessXmlReporter.java:294)
at org.apache.maven.plugin.surefire.report.StatelessXmlReporter.serializeTestClass(StatelessXmlReporter.java:204)
at org.apache.maven.plugin.surefire.report.StatelessXmlReporter.testSetCompleted(StatelessXmlReporter.java:158)
at org.apache.maven.plugin.surefire.report.StatelessXmlReporter.testSetCompleted(StatelessXmlReporter.java:51)
at org.apache.maven.plugin.surefire.report.TestSetRunListener.testSetCompleted(TestSetRunListener.java:193)
at org.apache.maven.plugin.surefire.booterclient.output.ForkClient$TestSetCompletedListener.handle(ForkClient.java:143)
at org.apache.maven.plugin.surefire.booterclient.output.ForkClient$TestSetCompletedListener.handle(ForkClient.java:127)
at org.apache.maven.plugin.surefire.booterclient.output.ForkedProcessEventNotifier.notifyEvent(ForkedProcessEventNotifier.java:197)
at org.apache.maven.plugin.surefire.booterclient.output.ForkClient.handleEvent(ForkClient.java:303)
at org.apache.maven.plugin.surefire.booterclient.output.ForkClient.handleEvent(ForkClient.java:59)
at org.apache.maven.plugin.surefire.booterclient.output.ThreadedStreamConsumer$Pumper.run(ThreadedStreamConsumer.java:86)
at java.base/java.lang.Thread.run(Thread.java:833)
Thank you.
The line numbers in the stack trace in the dump stream don't match the sources of
maven-surefire-plugin version:3.3.1
so you may have another problem.
PS: Just to add that this is how the xml file ends. (open test case tag and nothing else after it, this not even being the last test executed and which should be printed)
<testcase name="Validate fields for total pans by risk against materialised views" classname="Validate the AMLAR top and total pans aggregation endpoints and their DB materialised views" time="2.795"/>
<testcase name="Validate fields for total pans by risk against materialised views" classname="Validate the AMLAR top and total pans aggregation endpoints and their DB materialised views" time="2.795"/>
<testcase name="Validate fields for total pans by risk against materialised views for dmp" classname="Validate the AMLAR top and total pans aggregation endpoints and their DB materialised views" time="0">
<skipped message="'cucumber.filter.tags=( ( @NotImporter and @hadoop ) and not ( @skip ) )' did not match this scenario"/>
</testcase>
<testcase name="Example #1.1" classname="Examples" time="1.269"
Giving 0 as the number of retries does not seem to cause the issue, but anything other than that then it happens, unless all the tests pass on first attempt and a retry is not needed, so maybe something the retries are regenerating and corrupting the TEST.xml file?
@francislainy cheers!
I still can't reproduce the problem but I think I have a vague idea where the problem might be. One contributing factor seems to be that sure fire assumes test names to be unique. Can you try to reproduce your problem with all combinations of the following:
maven-surefire-plugin:3.5.0
maven-surefire-plugin:3.4.0
Set the cucumber.junit-platform.naming-strategy
to long
i.e:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.5.0</version>
<configuration>
<properties>
<configurationParameters>
cucumber.junit-platform.naming-strategy=long
</configurationParameters>
</properties>
</configuration>
</plugin>
So the trick is to have a skipped test included with flaky test.
Reproducer:
Feature: Example Belly
Scenario: a few cukes
Given I have 0 cukes in my belly
Scenario: a few cukes
Given I have 21 cukes in my belly
Scenario: a few cukes
Given I have 42 cukes in my belly
package io.cucumber.skeleton;
import io.cucumber.java.en.Given;
import org.junit.jupiter.api.Assumptions;
public class StepDefinitions {
@Given("I have {int} cukes in my belly")
public void I_have_cukes_in_my_belly(int cukes) {
if (cukes == 0) {
return;
}
if (cukes == 21) {
Assumptions.abort("Not now");
}
throw new RuntimeException("Oops");
}
}
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.1.0</version>
<configuration>
<rerunFailingTestsCount>2</rerunFailingTestsCount>
<properties>
<!-- Work around. Surefire does not include enough
information to disambiguate between different
examples and scenarios. -->
<configurationParameters>
cucumber.junit-platform.naming-strategy=long
</configurationParameters>
</properties>
</configuration>
</plugin>
@francislainy @dunja132015 as a work around ensure that each feature has a unique name and that with in a feature all scenarios have unique names. Then also set cucumber.junit-platform.naming-strategy
to long
to ensure the examples in scenario out lines are given unique names.
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.5.0</version>
<configuration>
<properties>
<configurationParameters>
cucumber.junit-platform.naming-strategy=long
</configurationParameters>
</properties>
</configuration>
</plugin>
@dunja132015 in addition, you'll also want to use JUnit 5 tag expressions to select your tests with Surefire instead of -Dcucumber.filter.tags="@XYZ"
. So you would use-DexcludedGroups="Ignore" -Dgroups="Smoke | Sanity"
.
See https://github.com/cucumber/cucumber-jvm/tree/main/cucumber-junit-platform-engine#tags.
@francislainy cheers!
I still can't reproduce the problem but I think I have a vague idea where the problem might be. One contributing factor seems to be that sure fire assumes test names to be unique. Can you try to reproduce your problem with all combinations of the following:
- Use
maven-surefire-plugin:3.5.0
- Use
maven-surefire-plugin:3.4.0
- Ensure all your scenario and feature names are unique.
- Set the
cucumber.junit-platform.naming-strategy
tolong
i.e:<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <version>3.5.0</version> <configuration> <properties> <configurationParameters> cucumber.junit-platform.naming-strategy=long </configurationParameters> </properties> </configuration> </plugin>
@mpkorstanje Thanks a lot! Yes, allowing for the longer names seems to be a good workaround. However, the names get printed a bit toooooo long due to the example scenarios, but I guess we can live with this until a proper fix for the issue is found. But while on it, what I've also noticed looking at some more of our report failures is they seem to only happened for scenario outline and example pieces, that being the case when the testcase does not close and the report fails to print.
You can also make your scenarios unique with the pickle name strategy.
<configurationParameters>
cucumber.junit-platform.naming-strategy=short
cucumber.junit-platform.naming-strategy.short.example-name=pickle
</configurationParameters>
But then you have to ensure that each of your scenario outlines uses a parameterized scenario name that is unique.
Scenario Outline: Failing test <a>
When step 1
Then step a: "<a>"
Examples:
| a |
| string11 |
| string12 |
Scenario Outline: Passing test <b>
When step 1
Then step b: "<b>"
Examples:
| b |
| string21|
| string22|
Created issue for Surefire: https://issues.apache.org/jira/browse/SUREFIRE-2260
But we'll probably have to add some suffixes to scenario names if they're not unique in Cucumber.
You can also make your scenarios unique with the pickle name strategy.
<configurationParameters> cucumber.junit-platform.naming-strategy=short cucumber.junit-platform.naming-strategy.short.example-name=pickle </configurationParameters>
But then you have to ensure that each of your scenario outlines uses a parameterized scenario name that is unique.
Scenario Outline: Failing test <a> When step 1 Then step a: "<a>" Examples: | a | | string11 | | string12 | Scenario Outline: Passing test <b> When step 1 Then step b: "<b>" Examples: | b | | string21| | string22|
Thank you @mpkorstanje . I'm not sure that would feasible in our case though, since a few of the scenarios require the same parameter name, such as
About the logging for the number of tests executed:
@Importer
Feature: Debug feature file
@debug
Scenario: A failed test
And I force a failure to happen
@debug
Scenario: A passing test
And I pass
public class ImporterStep {
private static int runCount = 0;
@Given("I pass")
public void pass() {
assertTrue(true);
}
@Given("I force a failure to happen")
public void testFlaky() {
runCount++;
if (runCount == 1) {
fail("Failing on the first attempt");
}
assertEquals(2, runCount);
// assertEquals(2, 1);
}
Then I get this for the TEST.xml file, which shows 1 test run (as there's only 1 test which needs a rerunning), even though I have two tests in my feature file.
<testsuite xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="https://maven.apache.org/surefire/maven-surefire-plugin/xsd/surefire-test-report-3.0.xsd" version="3.0" name="com.mycompany.AmlarTest" time="0.238" tests="1" errors="0" skipped="0" failures="0">
Scenario outlines are syntactic sugar for repeating the same scenario several times with some variables replaced. Then because the pickle
naming strategy is used, each example is named after the interpolated scenario outline (the thing you get when unrolling the scenario outline into individual scenarios). Because these are all the same, Surefire gets confused which scenario is actually being rerun.
One other possibility to distinguish the different Scenarios
generated by a Scenario Outline
/Scenario Template
is to use a FileLocation
(like: some.feature:<LINE_NUMER>
) that refers to the ExampleTable row that contains the parameters to generate this Scenario
.
But I am not sure if that would have any effect with SUREFIRE
.
The approach that @mpkorstanje suggested is probably better, like:
{ScenarioOutline.name}__@{ExamplesTable.index}_{ExampleTableRow.index}
or {ScenarioOutline.name}__@{ExampleTableRow.line_number}
or (hint: multiple ExampleTable
(s) may exist in a ScenarioOutline
).
👓 What did you see?
Have a feature test file like:
Java step Implementation:
Execute the test from command line (make sure you have surefire.rerunFailingTestsCount set):
>mvn test -Dsurefire.rerunFailingTestsCount=2 -Dcucumber.filter.tags="@XYZ"
TEST OUTPUT:
IMPORTANT: If the pipeline is used, test result is PASS (assuming due to 0 failures reported), and the next pipeline step is not blocked!!
NOTE: The issue seems to be already reported (and appearing fixed) here: https://github.com/cucumber/cucumber-jvm/issues/2709, but I'm reproducing it with the steps above. RunCucumberTest.xml file does not have the proper xml format, it ends with
<testcase name="Example #1.1" classname="Examples" time="0.408"
✅ What did you expect to see?
2 failed tests, 2 passed
📦 Which tool/library version are you using?
cucumber.version: 7.18.1 maven-surefire-plugin version: 3.3.1
🔬 How could we reproduce it?
No response
📚 Any additional context?
No response