cucumber / cucumber-jvm

Cucumber for the JVM
https://cucumber.io
MIT License
2.7k stars 2.02k forks source link

Unable to run Cucumber Feature via Command Line after upgrade to 7.11.0 #2689

Closed adarshhm6 closed 1 year ago

adarshhm6 commented 1 year ago

Unable to register the Spring Context beans after upgrade to cucumber 7.11.0 when running via CLI

With Cucumber-7.0.0, I was able to run the application from command line and reports were getting generated successfully. Since I wanted to hide the skipped/unknown scenarios(https://github.com/cucumber/html-formatter/issues/49) from HTML report which was added as part of latest release with 7.11.0, tried to upgrade cucumber version to 7.11.0.

Was using me.jvt.cucumber:reporting-plugin:7.0.0 which is wrapped on top of net.masterthought:cucumber-reporting:5.6.0. Now explicitly added net.masterthought:cucumber-reporting:5.7.4

πŸ‘“ What did you see?

When Scenarios/Features are run independently the tests run and spring beans are getting registered without any issue. But when it's run via CLI getting NullPointer Exception on initialization during invocation of Scenario Hooks.

Have maintained separate modules for utilities, features, glue steps.

βœ… What did you expect to see?

Reports are getting generated but those contains the Failed runs. Expected to run the features successfully.

πŸ“¦ Which tool/library version are you using?

Cucumber-core- 7.11.0 cucumber-java-7.11.0 cucumber-junit-7.11.0 cucumber-spring-7.11.0 me.jvt.cucumber:reporting-plugin-7.3.0 net.masterthought:cucumber-reporting-5.7.4 io.cucumber:gherkin-26.0.3 spring-boot-starter-test-2.5.3 spring-boot-starter-aop-2.5.3 spring-context-5.3.9 spring-aspects-5.3.9

πŸ”¬ How could we reproduce it?

Command used to run via CLI which is running without any issues with 7.0.0

java -DAUTOMATION_LOG_PATH=LogPath\logs -DAUTOMATION_CONFIG=ConfigPath\test-configuration-local.conf -cp CongFilePath\conf -cp JarPath\target\automation\lib\automation-glue-1.0-SNAPSHOT-fat.jar io.cucumber.core.cli.Main FeatureFilePath --glue com.automation -t ""@Sanity"" -p me.jvt.cucumber.report.PrettyReports:ReportsDirPath\pretty -p html:ReportsDirPath\html\report.html -p rerun:ReportsDirPath\rerun\rerun.txt

πŸ“š Any additional context?

All these 3 files are under com package of glue-steps module image

image

image

mpkorstanje commented 1 year ago

You're using a fat jar. Make sure it's constructed by correctly merging all files from META-INF. See also https://github.com/cucumber/cucumber-jvm/issues/2588#issuecomment-1192425183

adarshhm6 commented 1 year ago

Thanks for the response @mpkorstanje I just cross checked. I am already using apache-maven-shaded-plugin in pom.xml of gluesteps module as below. image

As mentioned, I have grouped glue steps, features and utility methods in independent modules and it's working fine with 7.0.0 version and able to run it via CLI and inside the jar. But with upgrade to 7.11.0 it's throwing NullPointer Exception for autowiring ExecutionContext class present in Util module from ScenarioHooks class in glue steps module. However inside the jar, both classes are present and merged properly just like with 7.0.0

image

Am I missing something that it fails to run with 7.11.0?

mpkorstanje commented 1 year ago

However inside the jar, both classes are present and merged properly just like with 7.0.0

The META-INF/services folder does not typically contain class files. I don't think you have understood the problem you have yet.

Please read and carefully consider the comments after https://github.com/cucumber/cucumber-jvm/issues/2588#issuecomment-1192425183. At the very least you should verify the packing is the problem by running your tests with a regular jar and adding the dependencies to the classpath.

adarshhm6 commented 1 year ago

@mpkorstanje I understood your point about META-INF part. But I just elaborated on where exactly am facing the issue. As you mentioned in #2588 I have already added maven-shade-plugin and here is the content of that in the jar. image

Are you suggesting like, I should explicitly add the cucumber related dependencies(cucumber-core, cucumber-java, cucumber-spring, cucumber-junit, reporting-plugin) as jar into the buildpath instead of pom dependency and build the jar and run the tests?

mpkorstanje commented 1 year ago

No, typically, rather than using a fat jar, you would run a java program using java -jar my-project.jar -cp dir-with-depenency-jars/. You can use this to convince yourself that it is the shading process that causes your problem.

Then once you are convinced that this is indeed the problem and look at how the maven shade plugin merges the META-INF/services directory you'll see that if multiple dependencies have the same services file name e.g. io.cucumber.core.ObjectFactory the shade plugin will overwrite these files rather than merge their contents.

You can tweak this by configuring the ServicesResourceTransformer on the Maven Shade Plugin. Though as you're using Spring, chances are you'll be incorrectly merging other files as well. If you absolutely must use a single jar file, consider using Springs executable jar format instead.

adarshhm6 commented 1 year ago

Thanks for detailed explanation @mpkorstanje which gives better understanding. In my project, I need to bundle multiple modules into one jar for shaded plugin is used. I tried to follow the steps which you suggested. Suggestion 1) Instead of maven-shade plugin, used spring-boot-maven-plugin and tried to run it as jar

image

image

When tried to run it as java -jar JarPath/automation-glue-1.0-SNAPSHOT.jar.jar, I got No Features found at classpath

So modified the command as below and tried to run it java -DAUTOMATION_LOG_PATH=LogPath\logs -DAUTOMATION_CONFIG=ConfigPath\test-configuration-local.conf -jar JarPath/automation-glue-1.0-SNAPSHOT.jar -t "@Sanity" --glue com.automation FeaturesPath which resulted in below error

image

Since, my glue module is dependent on util and test-domain module, I tried to add those as classpath with -cp java -DAUTOMATION_LOG_PATH=LogPath\logs -DAUTOMATION_CONFIG=ConfigPath\test-configuration-local.conf -cp Utils_Module_Path\automation-utils\target -jar JarPath/automation-glue-1.0-SNAPSHOT.jar -t "@Sanity" --glue com.automation FeaturesPath but again the same error as in above screenshot like doesn't support classpath scan of nested jars. So, Couldn't successfully run it as java program using Spring executable format

Suggestion 2) So followed the 2nd approach which is the work around you provided to tweak configuration to include ServicesResourceTransformer image And then I could see that under META_INF/services inside the io.cucumber.core.backend.ObjectFactory 2 entries which concludes like it was shading problem earlier where merging of entries was not proper. image

After that I was able to run the Cucumber Application via Command Line using the generated fat jar which is bundled with all modules(dependent modules) using initial command java -DAUTOMATION_LOG_PATH=LogPath\logs -DAUTOMATION_CONFIG=ConfigPath\test-configuration-local.conf -cp CongFilePath\conf -cp JarPath\target\automation\lib\automation-glue-1.0-SNAPSHOT-fat.jar io.cucumber.core.cli.Main FeatureFilePath --glue com.automation -t ""@sanity"" -p me.jvt.cucumber.report.PrettyReports:ReportsDirPath\pretty -p html:ReportsDirPath\html\report.html -p rerun:ReportsDirPath\rerun\rerun.txt

The shading problem was not there while using version 7.0.0, may be with new changes and dependencies it caused overwriting which is resolved with the help of ServicesResourceTransformer as you suggested.

and also you suggested If I have to use a single jar file, consider using Springs executable jar format instead. Using springs executable jar format, I faced the issue as I mentioned in Suggestion 1 that am not able to reference the dependent util jar modules via classpath. Is there any alternative way to reference the dependent modules in classpath and run it as Spring Executable Jar using java -jar JarPath/automation-glue-1.0-SNAPSHOT.jar.jar ?

My need is that I need to bundle all dependent modules into one jar which is the reason am using maven-shade-plugin which is one of the way to create executable jar. With inclusion of ServicesResourceTransformer with maven-shade-plugin issue is resolved, whether any issues in using maven-shade-plugin that you suggested for spring executable jar format?

mpkorstanje commented 1 year ago

Unfortunately these two facts are hard to change:

1) Correctly shading jar requires correctly configuring the shade plugin which is a non trivial endeavour.

2) Cucumber currently can't scan inside Spring Boot dependencies (only the root jar).

So given your situation you don't have many good choices. Fundamentally you could remove the need to deploy single jars and you would resolve your problems. But without knowing your circumstances I can't say if that is an option for you.

I am open to suggestions that make 2) possible but that too isn't trivial.

adarshhm6 commented 1 year ago

Currently shading jar requires correctly configuring the shade plugin which is a non trivial endeavour --> Agreed. As of now, solution for me is configuring ServicesResourceTransformer with apache maven-shade-plugin but not sure in future what problem will arise when I try to upgrade the cucumber version again.

Cucumber currently can't scan inside Spring Boot dependencies (only the root jar) --> Yes, that is the sad part where it's not scanning the nested jars in the classpath with Spring-boot-maven-plugin

With that limitation, to achieve Springs executable format then ideal solution would be to have a single module with different packages for config related files, features, glue-steps, util-code and domain related stuff instead of multi-module structure ?? If any other alternative ways for classpath scan of dependent modules(nested jars) with Spring executable format in multi module structure, then please do suggest.

The module structure incorporated in my project to bifurcate between different components like glue module, features, configuration related files, Utility Code. As of now am planning to continue with shade plugin with tweak in configuration ServicesResourceTransformer as you suggested and Hope there is no harm in using the shade plugin for bundling all modules to generate fat jar.

Thanks for your time and detailed guidelines which helped me to understand the issue and get this sorted out.

mpkorstanje commented 1 year ago

With that limitation, to achieve Springs executable format then ideal solution would be to have a single module with different packages for config related files, features, glue-steps, util-code and domain related stuff instead of multi-module structure ?? If any other alternative ways for classpath scan of dependent modules(nested jars) with Spring executable format in multi module structure, then please do suggest.

Yes. I do believe that a single module would work. I don't think there are any alternatives.

The module structure incorporated in my project to bifurcate between different components like glue module, features, configuration related files, Utility Code.

Only features and glue (step definitions), the other code can be anywhere. Maybe that helps.

adarshhm6 commented 1 year ago

Yes. I do believe that a single module would work. I don't think there are any alternatives.

Ok. Understood

Only features and glue (step definitions), the other code can be anywhere. Maybe that helps.

Isn't this contradicts the first statement? The other code(Utility modules which includes jdbc connections and common methods) also needs to be in the same glue(step definitions) module as currently I am adding the utility module as a dependency module in pom.xml of glue module and Cucumber scans only the root jar and it doesn't scan the nested dependent jars or classpath entries (Error screenshot where util lib is nested jar inside glue jar). Hence the only solution is to keep a single module with separate packages for glue and utility code.

image

So as you suggested for me the work around solution as of now is to use ServicesResourceTransformer in the maven-shade-plugin. With that may be this issue can be marked as resolved. Thank you @mpkorstanje for the detailed explanation and solution.

mpkorstanje commented 1 year ago

Isn't this contradicts the first statement?

Depends a bit on how your packages and glue property are configured. If your modules contain distinct package names that are siblings to each and the glue targets one of those and not other there shouldn't be problems.

Thank you @mpkorstanje for the detailed explanation and solution.

You're welcome!