Hot questions for Using Cucumber in gradle

Question:

Hi I want to run a few test made with cucumber on a gradle project, I was trying to run this test's on another PC (with limited network access) so I copy/pasted everything that was under ~./gradle/caches and the project files like build, src and build.gradle and placed everything on the same place as the original

So once I got everything in place I ran this

  gradle --no-daemon --offline cucumber

And I get a failure

> Could not resolve all dependencies for configuration ':classpath'.
   > Could not download gradle-cucumber-plugin.jar (com.github.samueltbrown:gradle-cucumber-plugin:0.9): No cached version available for offline mode

The odd thing is that I have the file on the caches directory

~/.gradle/caches/modules-2/files-2.1/com.github.samueltbrown/gradle-cucumber-plugin/0.9/7b65c67654715025eed1924
0c4f7defbef9645e0# ls
gradle-cucumber-plugin-0.9.jar

FYI, I don't have the kind of experience with gradle/java as I would like so any kind of suggestion would be great, this is where the plugin is "required" on the build.gradle

plugins {
  id "com.github.samueltbrown.cucumber" version "0.9"
}

Thanks


Answer:

See this open Gradle issue

Gradle cache isn't exactly portable currently. It does work, if the absolute path of the .gradle folder is exactly the same on both machines. The absolute path of ~/.gradle is not the same if the username on both machines is different!

There are a few ways you can get around this, in order of effort required, least to most:

1. Run without --offline once

If this is possible on your network limited machine, run without offline flag once, the dependencies will not be downloaded again, but checksums are - and more importantly the cache is now validated and suitable for use with the --offline flag.

2. Move .gradle to a repeatable path

Move gradle cache folder to something like /tmp/.gradle by setting GRADLE_USER_HOME on both machines and then copy gradle cache between machines.

3. Export all dependencies to a local folder

On the network connected machine, use a gradle task to export all dependencies to a filesystem folder. Copy the folder over to the other machine, and use a flatdir repo on the second machine.

repositories {
  flatDir {
    dirs 'lib' //folder containing dependencies
  }
}

(You could also export to a maven format repo instead of flatdir. See https://stackoverflow.com/a/13396776/1174024)

4. Use a maven cache instead

In my experience Maven's .m2 folder is far more portable. Create a maven project with the same dependencies, cache dependencies into .m2 and then copy .m2 over to the second machine. On the second machine add mavenLocal() as a repo.

Question:

I try to run this gradle task

task runCucumber(type: JavaExec) {
    main = "cucumber.api.cli.Main"
    args += ['-f', 'html:build/reports/cucumber/', '-f', 'json:build/reports/cucumber/report.json', '--glue', 'com.waze.testing.cucumber', 'src/main/resources/features'
             , '--tags', '~@ignore', '--tags', '~@preRelease', '--tags', '@advil']
    systemProperties['http.keepAlive'] = 'false'
    systemProperties['http.maxRedirects'] = '20'
    ignoreExitValue = true
}

and get this error:

Error: Could not find or load main class cucumber.api.cli.Main

why is that? I can find it in the included cucumber jar.

Edit

I already have this task that runs successfully:

mainClassName = "cucumber.api.cli.Main"

    run {
        args += ['-f', 'html:build/reports/cucumber/', '-f', 'json:build/reports/cucumber/report.json', '--glue', 'com.waze.testing.cucumber', 'src/main/resources/features'
                 , '--tags', '~@ignore', '--tags', '~@preRelease']
        systemProperties['http.keepAlive'] = 'false'
        systemProperties['http.maxRedirects'] = '20'
    }

Answer:

The following script works in terms of proper configuration but fails since there are now features/test to check.

apply plugin: 'java'

repositories {
    mavenCentral()
}

configurations {
    cucumber
}

dependencies {
    cucumber 'info.cukes:cucumber-java:1.2.2'
}

task runCucumber(type: JavaExec) {
    main = "cucumber.api.cli.Main"
    args += ['-f', 'html:build/reports/cucumber/', '-f', 'json:build/reports/cucumber/report.json', '--glue', 'com.waze.testing.cucumber', 'src/main/resources/features'
             , '--tags', '~@ignore', '--tags', '~@preRelease', '--tags', '@advil']
    systemProperties['http.keepAlive'] = 'false'
    systemProperties['http.maxRedirects'] = '20'
    ignoreExitValue = true
    classpath = configurations.cucumber
}

This is how JavaExec classpath should be modified.

Question:

I start with software testing - using Cucumber, Java, gradle. I try to learn this with the book "The Cucumber for Java Book"

But I try to do I with gradle instead of maven... But now I have some problems... I stick on page 149. I have to give so dependecies:

<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-webapp</artifactId>
<version>${jetty.version}</version>
</dependency>

I try to "translate" this to gradle

dependencies {
     testCompile group: 'junit', name: 'junit', version: '4.12'
     testCompile 'io.cucumber:cucumber-java:2.4.0'
     testCompile 'io.cucumber:cucumber-junit:2.4.0'
     testCompile group: 'info.cukes', name: 'cucumber-picocontainer', version: '1.2.5'
     compile group: 'org.eclipse.jetty', name: 'jetty-webapp', version: '9.4.12.v20180830'
}

Is this right? compile group: 'org.eclipse.jetty', name: 'jetty-webapp', version: '9.4.12.v20180830'

After that I have to run:

mvn exec:java -Dexec.mainClass="nicebank.AtmServer"

But how can I do this with gradle?

I hope someone can help me :)


Answer:

Your dependency looks good. Just one note: consider using implementation over compile as it improves the performance. Read about compile deprecation here.

You can also put your properties in gradle.properties file and reference them in the build script:

gradle.properties:

jettyVersion=9.4.12.v20180830

build.gradle:

implementation group: 'org.eclipse.jetty', name: 'jetty-webapp', version: jettyVersion

Jetty team also published BOMs: — org.eclipse.jetty:jetty-bom:9.4.12.v20180830 in your case. If you use multiple projects of the same version you can import the BOM and skip the version completely:

dependencies {
    implementation 'org.eclipse.jetty:jetty-bom:9.4.12.v20180830'
    implementation 'org.eclipse.jetty:jetty-webapp'
    implementation 'org.eclipse.jetty:jetty-runner'
}

As for the "exec" task: if you have only one main class in your project, like nicebank.AtmServer, consider using Gradle's Application Plugin:

plugins {
    id 'application'
}

mainClassName = 'nicebank.AtmServer'

This way you don't need to create "exec" task manually, you'll get one (run) from the plugin. As a bonus you'll get two "distribution" tasks that will create a ready-for-distribution archive with your app: distZip and distTar.

Question:

I'm fairly new to Gradle and am using version 5.0. I'm reasonably proficient in Cucumber.

I've got a simple project that builds a jar file and runs JUnit tests on them. It all works well. Now I want to add Cucumber to the project but I want my .feature files and the related stepdefs in an alternative source-tree (sourceSet in Gradle terminology).

The sources can be found on Github, which holds a sample project.

My source tree should look like this:

src/
    cucumberTest/
        java/
            /...
        resources/
            /...
    main/
        java/
            /...
        resources/
            /...
    test/
        /java
            /...
        resources/
            /...

When I put the .feature files in cucumberTest/resources and the stepdef java files in test/java my Cucumber tests run fine. But when the stepdefs are in cucumberTest/java Cucumber can't find the files and I get the error that they are not defined.

Undefined scenarios:
src/cucumberTest/resources/is_it_saturday_yet.feature:4 # Sunday isn't Saturday

2 Scenarios (1 undefined, 1 passed)
6 Steps (1 skipped, 2 undefined, 3 passed)
0m0.134s

My build.gradle file is this:

plugins {
    id 'java-library'
    id 'java'
    id 'idea'
}

repositories {
    jcenter()
    mavenCentral()
}

archivesBaseName = "helloworld"
version = '1.0'

dependencies {
    api 'org.apache.commons:commons-math3:3.6.1'

    implementation 'com.google.guava:guava:26.0-jre'

    testImplementation 'junit:junit:4.12'
    testCompile("junit:junit:4.12")
    testCompile('org.junit.jupiter:junit-jupiter-api:5.3.2')
    testCompile('org.junit.jupiter:junit-jupiter-params:5.3.2')

    testRuntime('org.junit.jupiter:junit-jupiter-engine:5.3.2')
    testRuntime("org.junit.vintage:junit-vintage-engine:5.3.2")

    testCompile 'io.cucumber:cucumber-java:4.2.0'
    testCompile 'io.cucumber:cucumber-junit:4.2.0'
}

configurations {
    cucumberRuntime {
        extendsFrom testRuntime
    }
}

test {
    useJUnitPlatform ()
    testLogging {
        events "passed", "skipped", "failed"
    }
}

// Cucumber stuff:

sourceSets {
    cucumberTest {
        compileClasspath += sourceSets.main.output
        runtimeClasspath += sourceSets.main.output
    }
}

configurations {
    cucumberTestImplementation.extendsFrom implementation
    cucumberTestRuntimeOnly.extendsFrom runtimeOnly
}

dependencies {
    testCompile 'io.cucumber:cucumber-java:4.2.0'
    testCompile 'io.cucumber:cucumber-junit:4.2.0'
}

task cucumberTest() {
    dependsOn assemble, compileTestJava
    doLast {
        javaexec {
            main = "cucumber.api.cli.Main"
            classpath = configurations.cucumberRuntime + sourceSets.main.output + sourceSets.test.output + sourceSets.cucumberTest.output
            args = ['--plugin', 'pretty', '--glue', 'stepdefs.hellocucumber', 'src/cucumberTest/resources']
        }
    }
}

wrapper {
    gradleVersion = '5.0'
}

I've looked all over the web, but I feel like I'm the only one who wants to use cucumber-jvm with Gradle 5 and have the BDD tests (cucumber) separated from the TDD tests (JUnit).

I can take the easy route and just mix them both, but apart from being a little puritan and have BDD and TDD separated, I also want to understand what's going on and why it's not working.

Help is appreciated. Thanks.

Iwan


Answer:

With the help of Bart Kors, a good friend of mine, I was able to get it to work as intended. I updated the Github repository to include the working code. Do a clone of TrheeAxis/hellocucumber to get the working code.

Question:

I am building a simple test app that uses cucumber. Unfortunately 'gradle cucumber' throws errors when I try to run it. However everything runs fine when I change testImplement to the deprecated testCompile in build.gradle. Is this expected behaviour? What would I have to do to get cucumber to run using testImplementation?

build.gradle:

dependencies {
    testImplementation 'io.cucumber:cucumber-java:4.2.0'
    testImplementation 'io.cucumber:cucumber-junit:4.2.0'
}

configurations {
    cucumberRuntime {
        extendsFrom testRuntime
    }
}

task cucumber() {
    dependsOn assemble, compileTestJava
    doLast {
        javaexec {
            main = "cucumber.api.cli.Main"
            classpath = configurations.cucumberRuntime + sourceSets.main.output + sourceSets.test.output
            args = ['--plugin', 'pretty', '--glue', 'gradle.cucumber', 'src/test/resources']
        }
    }
}

Result (Error):

> Task :cucumber FAILED
Error: Could not find or load main class cucumber.api.cli.Main
Caused by: java.lang.ClassNotFoundException: cucumber.api.cli.Main

build.gradle:

dependencies {
    testCompile 'io.cucumber:cucumber-java:4.2.0'
    testCompile 'io.cucumber:cucumber-junit:4.2.0'
}
...

Result (Works):

> Task :cucumber
No features found at [src/test/resources]

0 Scenarios
0 Steps

Can someone explain what is going on here? Any help is greatly appreciated!


Answer:

See here which says that testRuntime is deprecated

The compile, testCompile, runtime and testRuntime configurations inherited from the Java plugin are still available but are deprecated

I think it should be

configurations {
    cucumberRuntime {
        extendsFrom testImplementation
    }
}

Question:

I have inherited a framework that uses Cucumber to use a series of browser-based tests with Selenium, in Java. The framework is currently built using Gradle and this works well.

I would very much like to integrate this whole thing with Browserstack, but the only Gradle plugin that I can find (here) is for Espresso, and so not applicable to my issue. I have also found another repository (here) which does exactly what I want, but does so through Maven.

Note I am unfamiliar with both Gradle and Maven and hence am inclined to stick with the solution that is currently running and wary of unnecessary change. Bearing this in mind, I have the following questions:

  1. Is there a published way to integrate Cucumber (Java) with Browserstack using Gradle?
  2. If not, is converting from Gradle to Maven really as trivial as this article makes me believe? Is there anything else I must be aware of?

Answer:

Regarding your queries:

1- I am not aware of any published documentation to integrate Cucumber (Java) with Browserstack using Gradle

2- In the link that you have shared( https://dzone.com/articles/how-to-convert-maven-to-gradle-and-vice-versa), it says that you can convert maven to gradle in one step.

Run the command: gradle init

In the directory containing the POM. This will convert the Maven build to a Gradle build, generating a settings.gradle file and one or more build.gradle files.

3-You have also shared the link: https://github.com/browserstack/cucumber-java-browserstack which uses maven. You can follow the steps and easily convert maven to gradle

Question:

I am working with gradle in a java project, I run my cucumber tests with a gradle task, something like this:

task cucumber (){
//task that starts the app
dependsOn 'jettyRunDaemon'
 jvmArgs '-javaagent:E:/MyProject/build/jacoco/jacocoagent.jar=destfile=build/jacoco/jacoco.cucumber.exec'
doLast {
    javaexec {
        main = 'cucumber.api.cli.Main'
        classpath = sourceSets.main.output +
                    sourceSets.test.output +
                    configurations.testRuntime
        args = cucumberArgs()
    }
}
}

List<String> cucumberArgs() {
def args = [
    '--format', 'junit:build/cucumber-reports/junit/report.xml',
    '--format', 'html:build/reports/cucumber',
    '-f', 'pretty',
    '--glue', 'com.company.packageWithGlueJavaCode']

// Feature locations
args.add('src/test/resources/features')

return args
}

Does anybody knows if there is someway to get a Jacoco code coverage report of my cucumber tests by configuring gradle?. I know that for JUnit (test task) jacoco automaticly creates a exec file in jacoco/*.exec and I can get a report from it... but is there someway I can get, let's say a cucumber.exec file to get a report from it??


Answer:

Just add a JaCoCo Agent jar with this jvm Argument

-javaagent:[yourpath/]jacocoagent.jar=[option1]=[value1],[option2]=[value2]

to generate the .exec file.

Report Generation

Based on the collected *.exec files reports can be created the same way as for execution data collected with the Java agent. Note that for report generation the original class files have to be supplied, not the instrumented copies.

See http://www.eclemma.org/jacoco/trunk/doc/agent.html

Question:

I know that we can pass cucumber tags to Gradle by specifying the following in build.gradle file:

test {
    systemProperty "cucumber.options", System.getProperty("cucumber.options")
}

So when you run gradlew test -Dcucumber.options="--tags @CDC" it will only runs scenarios with CDC tag.

But my question is whether I can pass the cucumber options to the cucumber task. The reason I need to do this is because my cucumber task creates a JSON file after running the task, and I'm using gradle-cucumber-reporting plugin (https://github.com/SpacialCircumstances/gradle-cucumber-reporting) to generate a report based on the JSON file.

cucumber task

task cucumber() {
    dependsOn assemble, compileTestJava
    doLast {
        javaexec {
            main = "io.cucumber.core.cli.Main"
            classpath = configurations.cucumberRuntime + sourceSets.main.output + sourceSets.test.output
            args = [
            '--plugin', 'html:cucumber-report',
            '--plugin', 'json:cucumber.json', //generates a .json report file
            '--plugin', 'pretty',
            '--plugin', 'usage:cucumber-usage.json',
            '--glue', 'hellocucumber', 'src/test/resources']
        }
    }
}

Running the test task in Gradle doesn't update the JSON file.

Here's the snippet for gradle-cucumber-reporting:

cucumberReports {
  outputDir = file('cucumber-pretty-report')
  buildId = '0'
  reports = files('cucumber.json')
}

Answer:

You can add something like this to your cucumber task:

def tags = getProperty("tags")
args = [
'--plugin', 'html:cucumber-report',
'--plugin', 'json:cucumber.json', //generates a .json report file
'--plugin', 'pretty',
'--plugin', 'usage:cucumber-usage.json',
'--glue', 'hellocucumber', 'src/test/resources',
'--tags', tags]

Then you can run your task this way:

gradle cucumber -P tags=smoke

Question:

I've got a set of cucumber (java) tests which I would like to run via the commandline, setting system properties to define things such as the browser and whether to run locally or on BrowsersStack. The system is built with Gradle.

I have looked at this question and tried every different solution recommended there, to no avail. I also noticed that while the question was posed and the answer accepted in 2014, there are a number of 2018 comments that say the approach isn't working. A similar approach is detailed in this question, and the comments suggest it works for cucumber.

My gradle.build file looks like this:

buildscript {
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath 'org.junit.platform:junit-platform-gradle-plugin:1.0.0-RC2'
    }
}

plugins {
    id 'com.github.spacialcircumstances.gradle-cucumber-reporting' version '0.0.12'
}

repositories {
    mavenCentral()
}

apply plugin: 'org.junit.platform.gradle.plugin'

group 'org.myorg'
version '0.1'

junitPlatform {
    platformVersion '1.0.0-RC2'
    reportsDir file('build/test-results/junit-platform')
}

apply plugin: 'java'

sourceCompatibility = 1.8

cucumberReports {
    outputDir = file('target/cucumber-report')
    buildName = '0'
    reports = files('target/cucumber-report/json/cucumber-report.json')
    parallelTesting = false
}

test {
    ignoreFailures = true
    filter 
    {
        // Include all tests.
        includeTestsMatching "*.*"
    }
    systemProperty "localBrowser", System.getProperty("localBrowser")
}

dependencies {
    compile group: 'org.seleniumhq.selenium', name: 'selenium-java', version: '3.5.1'

    testCompile group: 'junit', name: 'junit', version: '4.12'
    testCompile group: 'org.testng', name: 'testng', version: '6.9.10'
    compile group: 'com.browserstack', name: 'browserstack-local-java', version:'1.0.1'

    // The cucumber version is 2.+ as 3+ cannot run tests from within IntelliJ ~_~
    // Fixed for v 4! :-)
    testCompile 'io.cucumber:cucumber-java:4.+'
    testCompile 'io.cucumber:cucumber-junit:4.+'
    testCompile group: 'io.cucumber', name: 'cucumber-picocontainer', version: '4.+'
    testCompile group: 'org.hamcrest', name: 'hamcrest-all', version: '1.3'

    testCompile group: 'org.junit.jupiter', name: 'junit-jupiter-api', version: '5.0.0-RC2'
    testCompile group: 'org.junit.platform', name: 'junit-platform-launcher', version: '1.0.0-RC2'
    testRuntime group: 'org.junit.jupiter', name: 'junit-jupiter-engine', version: '5.0.0-RC2'
    testRuntime group: 'org.junit.vintage', name: 'junit-vintage-engine', version: '4.12.0-RC2'

}

The code that checks the system property and should load it is held in a @Before statement:

@Before
    public void setUp(Scenario scenario) throws Exception {
        System.out.println(scenario);

        System.out.println("Value of localBrowser is " + System.getProperty("localBrowser"));

        // Local browser or Browserstack? Default is local.
        if (System.getProperty("localBrowser") == null) {
            System.setProperty("localBrowser", "true");
        }

         System.out.println("Value of localBrowser is " + System.getProperty("localBrowser"));
...

When I run the script using gradle test -DlocalBrowser=false --rerun-tasks the output looks like this:

> Task :junitPlatformTest
cucumber.runner.Scenario@2e377400
Value of localBrowser is null
Value of localBrowser is true
Tests running locally on Chrome

So, localBrowser is not picked up or not passed to the module, and my script then sets it to true as it finds a null value, and runs all tests on a local browser in the code that follows.

Am I missing something obvious, or is there a problem with a newer version of Gradle? I'm using version 5.0.


Answer:

After much digging, it works out that the junit gradle plugin was the thing blocking the passing of system variables. I have decided to post it as an answer because I'm sure this is not documented behaviour!

My new build file is as follows:

buildscript {
    repositories {
        mavenCentral()
    }
}

plugins {
    id 'java'
    id "com.github.spacialcircumstances.gradle-cucumber-reporting" version "0.1.2"
}


group 'org.myorg'
version '1.0-SNAPSHOT'

sourceCompatibility = 1.8

repositories {
    mavenCentral()
}

project.ext {
    cucumberVersion = '4.0.0'
}


dependencies {
    compile group: 'org.seleniumhq.selenium', name: 'selenium-java', version: '3.5.1'
    testCompile group: 'junit', name: 'junit', version: '4.12'
    testCompile 'io.cucumber:cucumber-java:' + cucumberVersion
    testCompile 'io.cucumber:cucumber-junit:' + cucumberVersion
    testCompile group: 'com.browserstack', name: 'browserstack-local-java', version:'1.0.1'
    testCompile group: 'io.cucumber', name: 'cucumber-picocontainer', version: '2.1.0'
    testCompile group: 'org.hamcrest', name: 'hamcrest-all', version: '1.3'
    testCompile group: 'org.testng', name: 'testng', version: '6.9.10'

}

test {
    systemProperties System.getProperties()
    ignoreFailures = true
    filter
            {
                // Include all tests.
                includeTestsMatching "*.*"
            }

    systemProperty "localBrowser", System.getProperty("localBrowser")
    testLogging.showStandardStreams = true
}

cucumberReports {
    outputDir = file('target/cucumber-report')
    buildId = '0'
    reports = files('target/cucumber-report/json/cucumber-report.json')
}

As far as I can tell, the junit gradle plugin was only used to enable the logging of test steps to the console. This has been resolved with including testlogging.showStandardStreams=true in the test task. There's a really neat logging plugin (here) that might do an even better job of this but I haven't tried it.

Question:

I currently have a large gradle project (using Java and IntelliJ) into which I would like to add Cucumber tests. I've taken some basic steps outlined by this link:

http://www.thinkcode.se/blog/2014/08/24/cucumberjvm-hello-world-with-gradle

such as adding dependency and repository settings to my build.gradle file like so:

dependencies {
    testCompile 'info.cukes:cucumber-java:1.2.4'
    testCompile 'info.cukes:cucumber-junit:1.2.4'
    testCompile 'junit:junit:4.12'
}
test {
    testLogging.showStandardStreams = true
    systemProperties System.getProperties()
}

However, I am not sure how to proceed from here. Is there a certain location where I should be creating my Cucumber tests??


Answer:

Boaz's answer above seems to work. Thanks!

define your feature files under the resources subtree and your steps at the same location as normal junit tests - the tests subtree