vscode-java-test
vscode-java-test copied to clipboard
Test Explorer not listing Cucumber-JVM tests
Repro:
- Create Java project
- Maven
- Cucumber.io archetype (7.11.1)
- everything default
Installed plugins (EVERYTHING else disabled):
Test runner for java version: v0.42.0
Also tried v0.42.2024080609 (pre-release)
Issue:
- Test explorer shows no tests.
- Clicking run briefly shows 0/1 tests passed
- Then switches to 0/0 tests
Generated project: demo.zip
Using Maven to create a normal JUnit5 test seems to work fine, this only appears to affect cucumber tests
This appears to be a JUnit5 specific issue. If I switch to JUnit4, the Test Explorer works just fine
i also have this problem, i think it is not really the version of junit the problem, because when i run only pure junit5 test the test explorer work just fine but when i run cucumber test with junit5 then i think vs code is unable to parse the raw test output because of cucumber + junit5.
Here is what i get as raw test output for junit5 pure test: %TESTC 3 v2 %TSTTREE2,fr.gsavin.vscode_test.VscodeTestApplicationTests,true,3,false,1,VscodeTestApplicationTests,,[engine:junit-jupiter]/[class:fr.gsavin.vscode_test.VscodeTestApplicationTests] %TSTTREE3,test(fr.gsavin.vscode_test.VscodeTestApplicationTests),false,1,false,2,test(),,[engine:junit-jupiter]/[class:fr.gsavin.vscode_test.VscodeTestApplicationTests]/[method:test()] %TSTTREE4,test2(fr.gsavin.vscode_test.VscodeTestApplicationTests),false,1,false,2,Example #1.5,,[engine:junit-jupiter]/[class:fr.gsavin.vscode_test.VscodeTestApplicationTests]/[method:test2()] %TSTTREE5,contextLoads(fr.gsavin.vscode_test.VscodeTestApplicationTests),false,1,false,2,contextLoads(),,[engine:junit-jupiter]/[class:fr.gsavin.vscode_test.VscodeTestApplicationTests]/[method:contextLoads()] %TESTS 3,test(fr.gsavin.vscode_test.VscodeTestApplicationTests)
%FAILED 3,test(fr.gsavin.vscode_test.VscodeTestApplicationTests)
%EXPECTS
true
%EXPECTE
%ACTUALS
false
%ACTUALE
%TRACES
org.opentest4j.AssertionFailedError: expected:
%TRACEE %TESTE 3,test(fr.gsavin.vscode_test.VscodeTestApplicationTests)
%TESTS 4,test2(fr.gsavin.vscode_test.VscodeTestApplicationTests)
%TESTE 4,test2(fr.gsavin.vscode_test.VscodeTestApplicationTests)
%TESTS 5,contextLoads(fr.gsavin.vscode_test.VscodeTestApplicationTests)
%TESTE 5,contextLoads(fr.gsavin.vscode_test.VscodeTestApplicationTests)
%RUNTIME7524
Here is what i got with junit4 + cucumber:
%TESTC 5 v2 %TSTTREE1,fr.gsavin.vscode_test.Cucumber4Tests,true,1,false,-1,fr.gsavin.vscode_test.Cucumber4Tests,, %TSTTREE2,Test,true,5,false,-1,Test,, %TSTTREE3,Sunday is not friday #1(Test),false,1,false,-1,Sunday is not friday #1(Test),, %TSTTREE4,Sunday is not friday #2(Test),false,1,false,-1,Sunday is not friday #2(Test),, %TSTTREE5,Sunday is not friday #3(Test),false,1,false,-1,Sunday is not friday #3(Test),, %TSTTREE6,Sunday is not friday #4(Test),false,1,false,-1,Sunday is not friday #4(Test),, %TSTTREE7,Sunday is not friday #5(Test),false,1,false,-1,Sunday is not friday #5(Test),, %TESTS 3,Sunday is not friday #1(Test)
%TESTE 3,Sunday is not friday #1(Test) %TESTS 4,Sunday is not friday #2(Test)
%TESTE 4,Sunday is not friday #2(Test) %TESTS 5,Sunday is not friday #3(Test)
%FAILED 5,Sunday is not friday #3(Test)
%TRACES
org.opentest4j.AssertionFailedError: expected:
%TRACEE
%TESTE 5,Sunday is not friday #3(Test) %TESTS 6,Sunday is not friday #4(Test)
%FAILED 6,Sunday is not friday #4(Test)
%TRACES
org.opentest4j.AssertionFailedError: expected: <false_test> but was:
%TRACEE
%TESTE 6,Sunday is not friday #4(Test) %TESTS 7,Sunday is not friday #5(Test)
%FAILED 7,Sunday is not friday #5(Test)
%TRACES
org.opentest4j.AssertionFailedError: expected:
%TRACEE
%TESTE 7,Sunday is not friday #5(Test)
%RUNTIME7476
And here is what i got with junit5 + cucumber: %TESTC 5 v2 %TSTTREE2,fr.gsavin.vscode_test.CucumberTests,true,1,false,1,CucumberTests,,[engine:junit-platform-suite]/[suite:fr.gsavin.vscode_test.CucumberTests] %TSTTREE3,Cucumber,true,1,false,2,Cucumber,,[engine:junit-platform-suite]/[suite:fr.gsavin.vscode_test.CucumberTests]/[engine:cucumber] %TSTTREE4,Test,true,1,false,3,Test,,[engine:junit-platform-suite]/[suite:fr.gsavin.vscode_test.CucumberTests]/[engine:cucumber]/[feature:classpath%3Afeatures%2Ftest.feature] %TSTTREE5,Sunday is not friday,true,1,false,4,Sunday is not friday,,[engine:junit-platform-suite]/[suite:fr.gsavin.vscode_test.CucumberTests]/[engine:cucumber]/[feature:classpath%3Afeatures%2Ftest.feature]/[scenario:2] %TSTTREE6,Examples,true,5,false,5,Examples,,[engine:junit-platform-suite]/[suite:fr.gsavin.vscode_test.CucumberTests]/[engine:cucumber]/[feature:classpath%3Afeatures%2Ftest.feature]/[scenario:2]/[examples:8] %TSTTREE7,Example #1.1,false,1,false,6,Example #1.1,,[engine:junit-platform-suite]/[suite:fr.gsavin.vscode_test.CucumberTests]/[engine:cucumber]/[feature:classpath%3Afeatures%2Ftest.feature]/[scenario:2]/[examples:8]/[example:10] %TSTTREE8,Example #1.2,false,1,false,6,Example #1.2,,[engine:junit-platform-suite]/[suite:fr.gsavin.vscode_test.CucumberTests]/[engine:cucumber]/[feature:classpath%3Afeatures%2Ftest.feature]/[scenario:2]/[examples:8]/[example:11] %TSTTREE9,Example #1.3,false,1,false,6,Example #1.3,,[engine:junit-platform-suite]/[suite:fr.gsavin.vscode_test.CucumberTests]/[engine:cucumber]/[feature:classpath%3Afeatures%2Ftest.feature]/[scenario:2]/[examples:8]/[example:12] %TSTTREE10,Example #1.4,false,1,false,6,Example #1.4,,[engine:junit-platform-suite]/[suite:fr.gsavin.vscode_test.CucumberTests]/[engine:cucumber]/[feature:classpath%3Afeatures%2Ftest.feature]/[scenario:2]/[examples:8]/[example:13] %TSTTREE11,Example #1.5,false,1,false,6,Example #1.5,,[engine:junit-platform-suite]/[suite:fr.gsavin.vscode_test.CucumberTests]/[engine:cucumber]/[feature:classpath%3Afeatures%2Ftest.feature]/[scenario:2]/[examples:8]/[example:14] %TESTS 7,Example #1.1
%TESTE 7,Example #1.1
%TESTS 8,Example #1.2
%TESTE 8,Example #1.2
%TESTS 9,Example #1.3
%FAILED 9,Example #1.3
%EXPECTS
true
%EXPECTE
%ACTUALS
false
%ACTUALE
%TRACES
org.opentest4j.AssertionFailedError: expected:
%TRACEE %TESTE 9,Example #1.3
%TESTS 10,Example #1.4
%FAILED 10,Example #1.4
%EXPECTS
false_test
%EXPECTE
%ACTUALS
test
%ACTUALE
%TRACES
org.opentest4j.AssertionFailedError: expected: <false_test> but was:
%TRACEE %TESTE 10,Example #1.4
%TESTS 11,Example #1.5
%FAILED 11,Example #1.5
%EXPECTS
true
%EXPECTE
%ACTUALS
false
%ACTUALE
%TRACES
org.opentest4j.AssertionFailedError: expected:
%TRACEE %TESTE 11,Example #1.5
%RUNTIME7578
From general the raw output seem correct and similar the only difference when it don't work i think it is in %TSTREE when junit5 + cucumber are much more "complicated" and i think it is when vs code extension don't seems to follow up
And on the following image we can just saw that vs code is stuck to CucumberTests(cucumber + junit5 test) as the icon on the left mean pending (maybe i am not sure about the icon signification), in all case the icon is different from the other one (Test: Cucumber + junit4, VscodeTestApplicationTest are junit5 pure test)
From general the raw output seem correct and similar the only difference when it don't work i think it is in %TSTREE when junit5 + cucumber are much more "complicated" and i think it is when vs code extension don't seems to follow up
If you are using the cucumber-junit-platform-engine, then Cucumber presents itself as an engine on the JUnit Platform. It can not be "more complicated".
However unlike the JUnit Jupiter Engine, Cucumber doesn't have class sources, but rather provides file or classpath sources. This has been tripping up various build tools and IDEs already https://github.com/junit-team/junit-framework/issues/2849.
As expected, the the plugin is parsing JUnits UniqueId here:
https://github.com/microsoft/vscode-java-test/blob/e3eb3bd13171cd8775d5cd051907f18d6e9c366d/src/runners/junitRunner/JUnitRunnerResultAnalyzer.ts#L187-L193
As this does not include the Suite Engine (engine:junit-platform-suite), we end up here:
https://github.com/microsoft/vscode-java-test/blob/e3eb3bd13171cd8775d5cd051907f18d6e9c366d/src/runners/junitRunner/JUnitRunnerResultAnalyzer.ts#L252-L259
Which assumes there is a class name which Cucumber doesn't have obviously.
Also worth noting the current test uses Cucumbers JUnit implementation 4 through the JUnit Vintage Engine, not the Cucumber JUnit Platform Engine for JUnit 5.
https://github.com/microsoft/vscode-java-test/blob/e3eb3bd13171cd8775d5cd051907f18d6e9c366d/test/test-projects/junit/src/test/java/junit5/cucumber/CucumberTest.java#L7-L9
For JUnit 5 this would look something like:
@Suite
@IncludeEngines("cucumber")
@SelectPackages("features")
@ConfigurationParameter(key = GLUE_PROPERTY_NAME, value = "junit5.cucumber")
public class RunCucumberTest {
}
What i mean by "more complicated" is the presence of special character like in this part "[feature:classpath%3Afeatures%2Ftest.feature]/[scenario:2]/[examples:8]" and it is a little longer than when using junit4 or just pure junit5 test.
For the test runner (junit5 + cucmber ) i use for the output i gave earlier i use pretty much what you gave:
@Suite @IncludeEngines("cucumber") @SelectPackages("features") @ConfigurationParameter(key = GLUE_PROPERTY_NAME, value = "fr.gsavin.vscode_test.steps" ) public class CucumberTests {
}
i can also say that even with junit4 + cucumber, the test result don't give much more information (i don't have any trace when a test failed for exemple) i just know if the test succeed or not but it is better than with junit5 + cucumber anyway
Ah. No, from my comments you should take that the vscode-java-test plugin is written with the wrong assumptions about JUnit 5. And because of that it only supports engine:junit-jupiter and engine:jqwik.
ah ok thanks for the information so right now, cucumber fall in failsafe generic regex that was what you mean ?
Yes. And further more both getTestIdForJunit5Method and getTestIdForNonJunit5Method are trying to extract class and method information out of JUnits UniqueId. However these are merely incidental details.
On the JUnit Platform tests are described by the TestIdentifier which has separate attributes for displayName, source, tags, type, id, ect. Ideally vscode-java-test would use the source to determine the class and method name (where applicable) and treat the id as an opaque element.
Anyway, I hope that information is helpful to someone working on a fix.
Hi @evilC, I'm an AI Support assistant here to help with your issue. While the team reviews your request, I wanted to provide some possible tips and documentation that might help you in the meantime.
The Java Test Runner extension doesn’t natively pick up Cucumber-JVM “@RunWith(Cucumber.class)” tests. To enable discovery you can switch to the JUnit Platform engine support for Cucumber:
• Add the JUnit Platform engine dependency in your pom.xml:
<dependency>
<groupId>io.cucumber</groupId>
<artifactId>cucumber-junit-platform-engine</artifactId>
<version>7.11.1</version>
<scope>test</scope>
</dependency>
• Create a JUnit Platform suite class (e.g. RunCucumberTest.java):
import org.junit.platform.suite.api.SelectClasspathResource;
import org.junit.platform.suite.api.ConfigurationParameter;
import org.junit.platform.suite.api.Suite;
import static io.cucumber.junit.platform.engine.Constants.*;
@Suite
@SelectClasspathResource("features")
@ConfigurationParameter(key = GLUE_PROPERTY_NAME, value = "com.example")
public class RunCucumberTest { }
• (Optional) Add a junit-platform.properties under src/test/resources:
cucumber.features=classpath:features
cucumber.glue=com.example
After this the Test Explorer should list and run your Cucumber scenarios via JUnit Platform.
No existing issue appears to be a true duplicate.
Other references with low confidence
- Issue #1008: “Test report doesn’t display latest test result when Cucumber + JUnit5 is used” – similar Cucumber+JUnit 5 setup but focuses on reporting, no definitive solution.
https://github.com/microsoft/vscode-java-test/issues/1008
The team will respond to your issue shortly. I hope these suggestions are helpful in the meantime. If this comment helped you, please give it a 👍. If the suggestion was not helpful or incorrect, please give it a 👎. Your feedback helps us improve!
The AI is not just wrong. Each time you zoom in there is another layer of misunderstanding. It is fractally-wrong.