Search code examples
jarcucumberbuild.gradlecucumber-jvmgradle-kotlin-dsl

Cucumber executable jar has undefined scenarios


I try to create an executable jar for my cucumber kotlin application but it prints in log that all scenarios are undefined:

...
3 Scenarios (3 undefined)
8 Steps (5 skipped, 3 undefined)
...

The application can be executed correctly from command line or from IntelliJ

Entry point of program:

package dsdms.client
import io.cucumber.core.cli.Main

class Main{
    companion object{
        @JvmStatic
        fun main(args: Array<String>) {
            Main.main("-p", "pretty",
                "--glue", "dsdms.client.cucumber",
                "--plugin", "html:build/reports/cucumber",
                "classpath:features")
            println("Hello Worldddd2!")

        }
    }
}

For project building I use Gradle with kotlin DSL

I tried to use first src/test and then src/main folders but even if I can execute all correctly form console it doesn't work for the jar execution

For fat jar generation I use:

tasks.withType<Jar> {
    duplicatesStrategy = DuplicatesStrategy.EXCLUDE
    manifest {
        attributes["Main-Class"] = "dsdms.client.Main"
    }

    dependsOn(configurations.runtimeClasspath)
    from({
        configurations.runtimeClasspath.get().filter { it.name.endsWith("jar") }.map { zipTree(it) }
    })

    destinationDirectory.set(file("$buildDir/output"))
}

Jar file from inside seems to have all files needed for execution screenshot of jar file in zip format

Gradle module structure

Here link to repository that reproduce my problem. It also contains a jar file produced by project


Solution

  • When creating your jar file you've set duplicatesStrategy = DuplicatesStrategy.EXCLUDE so any duplicate files will get ignored.

    If you set:

    duplicatesStrategy = DuplicatesStrategy.WARN
    

    You'll see that:

    > Task :SystemTester:jar
    Encountered duplicate path "META-INF/versions/9/module-info.class" during copy operation configured with DuplicatesStrategy.WARN
    Encountered duplicate path "META-INF/services/io.cucumber.core.backend.BackendProviderService" during copy operation configured with DuplicatesStrategy.WARN
    Encountered duplicate path "META-INF/versions/9/module-info.class" during copy operation configured with DuplicatesStrategy.WARN
    Encountered duplicate path "META-INF/versions/9/module-info.class" during copy operation configured with DuplicatesStrategy.WARN
    

    And these warnings matter! To understand why it is important to understand what these files do. So it would be worth reading the JAR File Specification.

    But in short for your problem, this is the warning that matters:

    Encountered duplicate path "META-INF/services/io.cucumber.core.backend.BackendProviderService" during copy operation configured with DuplicatesStrategy.WARN
    

    Because if you look at your dependencies you can see that you've got:

    [libraries]
    cucumber-java = {module = "io.cucumber:cucumber-java",  version.ref="cucumber"}
    ...
    cucumber-java8 = {module = "io.cucumber:cucumber-java8",  version.ref="cucumber"}
    

    Both of which include the META-INF/services/io.cucumber.core.backend.BackendProviderService file.

    For cucumber-java this file contains:

    io.cucumber.java.JavaBackendProviderService
    

    While for cucumber-java8 it contains:

    io.cucumber.java8.Java8BackendProviderService
    

    These tell Cucumber what which services it has available to detect step definitions (i.e. it lets Cucumber know you using annotations or lambdas)`.

    Now if you unzip the jar file you've made (you can do this using your favorite zip application) you'll find it only contains the BackendProviderService registration for cucumber-java and not the one for cucumber-java8. As such your step definitions are not loaded because Cucumber doesn't it understands lambda step definitions.

    The quick, and wrong way, to fix this would be to remove cucumber-java as a dependency. But you may run into other problems of a similar nature. So your best option would be to expand your Gradle task to merge META-INF/services files.

    How? Not a clue. I don't use Gradle.