Hot questions for Using Cucumber in groovy

Question:

I have a cucumber test that runs with @RunWith(Cucumber). The test uses static field from an other class. It looks like:

Cucumber test

@RunWith(Cucumber)
@CucumberOptions(
  features = 'src/test/resources/features/cucumber-test.feature',
  glue = ['src/test/groovy']
)
class CucumberTest {
  @BeforeClass
  static void setUp() {
    StaticClass.filed
  }
}

Static class

class StaticClass {
  static {
    filed = UUID.randomUUID().toString()
    println "Field initialized with value $filed in ${this.classLoader.toString()}\n"
  }
  static String filed
}

The cucumber-test.feature only contains line Feature: None and there are no step definitions. When I run this test, the output is

Field initialized with value 649b6d18-fe5a-4993-a92e-74645c3ab07d in groovy.lang.GroovyClassLoader$InnerLoader@534a5a98
Field initialized with value 9639e4de-661f-4ac7-afc9-715cdd17bb35 in sun.misc.Launcher$AppClassLoader@4e25154f

So static block was executed two times, but with different classloaders. It looks like there's one classloader for JUnit and the other one for Cucumber runner.

Also, if I comment out the line StaticClass.filed in setUp method the static block is only executed once. This time only with Groovy classloader

Field initialized with value 73d1d302-826c-4ec3-a9db-c065b399487f in groovy.lang.GroovyClassLoader$InnerLoader@5a45133e

The dependencies that I have in my project are:

dependencies {
  compile 'info.cukes:cucumber-groovy:1.2.5'
  compile 'info.cukes:cucumber-junit:1.2.5'
  compile 'org.codehaus.groovy:groovy-all:2.4.12'
}

Is there a way to use the same class loader for both JUnit and Cucumber runner?


Answer:

Take a look at your CucumberOptions:

glue = ['src/test/groovy']

This statement results in spawning a separate Groovy class loader to get classes (and thus recompile them) directly from specified source folder

All you need is to instruct Cucumber to take Groovy step definitions from classpath instead

glue = ['classpath:your.step.definitions.package.name']

Or even simplier:

glue = ['classpath:'] 

- if you dont want to care about package names at all. But note that last one may not be acceptable at many cases, as it will trigger scan/load of all the available classpath, so sticking with the precise package name(s) for step definitions is generally preferred

Question:

I have some cucumber tests with step definitions written on groovy, and there is the following error when I run it:

groovy.lang.MissingMethodException: No signature of method: test.steps.StepDefs$_run_closure14.doCall() is applicable for argument types: (null, null, String, Integer, String) values: [null, null, qwer, 21, 1]
Possible solutions: doCall(java.lang.String, java.lang.String, java.lang.String, short, java.lang.String), findAll(), findAll()

Scenario:

Scenario: justscenario
    Then ABC string qwer, short 21 version 1

Step definition:

Then(~'^ABC(?: ([^\\s]*))?(?: for "(\\S+)")? string ([^\\s]*), short (\\d+) version ([^\\s]*)$')
    { String key, String user, String stringId, short shortId, String version -> ...

In this case I don't need first two values so I omit it. Why first two nulls weren't cast to String? null can be casted to any reference, isn't it?

Cucumber version is 4.8.0 Cucumber-groovy is 4.7.1 Junit is 4.12


Answer:

The error is due to the use of short in the function to be called.

... test.steps.StepDefs$_run_closure14.doCall() is applicable for argument types: (null, null, String, Integer, String) values: [null, null, qwer, 21, 1]

Possible solutions: doCall(java.lang.String, java.lang.String, java.lang.String, short, java.lang.String), findAll(), findAll()

Question:

I'm having a problem with setting up the developing environment of my own. I'm using eclipse phontom with eclipse-groovy and Cucumber Eclipse plugin, and want to join the development that write test codes using groovy and cucumber.

At first I import dependency for cucumber (io.cucumber.cucumber-groovy:4.5.3) through maven , and everything works fine. However, some days later, colleagues had told me that they used to import dependency with older version (info.cukes.cucumber-groovy:1.2.4). And everything is broken in my laptop after downgrade.

For example, following is a simple step definition of Given.

package steps

import cucumber.api.groovy.EN
import cucumber.api.groovy.Hooks

this.metaClass.mixin(EN)
this.metaClass.mixin(Hooks)

Given(/an item ID which does not exist/) { ->
    documentId = "test_not_exist_" + UUID.randomUUID().toString()
}

And when I run it on my laptop, the following exception is raised.

No signature of method: steps.RawItemRetrieval.Given() is applicable for argument types: (java.lang.String, steps.RawItemRetrieval$_run_closure1) values: [an item ID which does not exist, steps.RawItemRetrieval$_run_closure1@45d2ade3]
Possible solutions: grep(), run(), run(), find(), every()
    at cucumber.runtime.groovy.GroovyBackend.loadGlue(GroovyBackend.java:85)
    at cucumber.runtime.Runtime.<init>(Runtime.java:91)
    at cucumber.runtime.Runtime.<init>(Runtime.java:69)
    at cucumber.runtime.Runtime.<init>(Runtime.java:65)
    at cucumber.api.cli.Main.run(Main.java:35)
    at cucumber.api.cli.Main.main(Main.java:18)

Is there anything I should do to make things work under dependency info.cukes.cucumber-groovy:1.2.4?

Following are my development environment which may be relevant:

JDK: Amazon Corretto-8.212.04.2 (build 1.8.0_212-b04)
Maven: 3.3.9
Groovy compiler in eclipse: 2.4.17

Any suggestions would be appreciated. Thanks!


Answer:

OK, I think I have found the reason. Maybe it is caused by cucumber 1.2.4 does not support step definition in the following format:

Given(/an item ID which does not exist/) { ->
    ....
}

When I change the style to regex format, it works again.

Given(~/^an item ID which does not exist$/) { ->
    ....
}

Thanks Mark Rotteveel for the suggestion!

Question:

I'm trying to create a Cucumber step definition in Groovy that finds a certain node in an XML document (a Solr search result) by the value of a mandatory 'url' tag and returns this node's position in the list of results.

This is my scenario:

    Given I seach for "pris"
    Then I should see this link in the results: "/7292/pris-forbruk-og-inntekt"
    Then I will see the position of this url: "/7292/pris-forbruk-og-inntekt"

This is my step definition:

Then(~'^I should see this link in the results: "([^"]*)"$') { String tekst ->
assertThat(browser.getPageSource(), containsString(tekst))
}

Then(~'^I will see the position of this url: "([^"]*)"$') { String text -> 

    pageSource = browser.getPageSource()

    //Prints the position of the relevant search result
    def xpathString = "count(/response/result/doc/str[.='" + text + "']/parent::*/preceding-sibling::*)"  //+1

    builder = DocumentBuilderFactory.newInstance().newDocumentBuilder()
    doc = builder.parse(new InputSource(new ByteArrayInputStream(pageSource.getBytes("utf-8"))))
    expr = XPathFactory.newInstance().newXPath().compile(xpathString)
    posisjon = expr.evaluate(doc)
    println "The position of this result is: " + posisjon.toString()
    println xpathString

}

Both tests return a green cuke, so I know that the string is found in the XML. Here's an example of the XML result I'm evaluating:

<response>
    <lst name="responseHeader">
        <int name="status">0</int>
        <int name="QTime">75</int>
        <lst name="params">
            <str name="indent">true</str>
            <str name="q">pris</str>
            <str name="wt">xml</str>
        </lst>
    </lst>
    <result name="response" numFound="306" start="0">
        <doc>
            <str name="url">/nasjonalregnskap/nokkeltall/priser-og-prisindekser</str>
            <str name="id">3730</str>
            <str name="tittel">Priser og prisindekser</str>
            <str name="innholdstype">nokkeltallsside</str>
            <date name="publiseringsdato">2010-11-23T11:35:00Z</date>
            <str name="hovedemner">Nasjonalregnskap</str>
            <str name="sprak">no</str>
            <str name="rom">statistikk</str>
        </doc>
        <doc>
            <str name="url">/7292/pris-forbruk-og-inntekt</str>
            <str name="id">7292</str>
            <str name="tittel">Pris, forbruk og inntekt</str>
            <str name="innholdstype">publikasjon</str>
            <date name="publiseringsdato">2002-06-13T10:00:00Z</date>
            <str name="hovedemner">Befolkning</str>
            <str name="sprak">no</str>
            <str name="rom">statistikk</str>
        </doc>
    </result>
</response>

When I run this test (we're using Maven to build and run, so I have limited debugging options afaik) I get this output from the two println commands:

 The position of this result is: 0   
 count(/response/result/doc/str[.='/7292/pris-forbruk-og-inntekt']/parent::*/preceding-sibling::*)

However, when I evaluate this Xpath query to the exact same XML document in Altova XMLspy, I get the expected result!

Apparently I don't have enough reputation (sorry, sorry...) to post images, here's a link to a screenshot of the result in Altova XMLspy

So the question I can't figure out is: Does the Java XPathFactory evaluate xpath differently from other interpreters? Have I made a mistake or error in my implementation? The answer eludes me :)


Answer:

Figured it out eventually. I was probably implementing XPathFactory.Evaluate in an incorrect way.

Either way, I ended up using Gpath instead, this is the code I used to return the right result, in case someone comes along with a similar request in the future:

String pageSource = browser.getPageSource()

 def stream = new XmlSlurper().parseText(pageSource)
 List test = stream.depthFirst().findAll {it.@name=="url"}
 def pos = test.findIndexOf { it.toString().contains(tekst) } + 1

println pos

Tell the future I say hi ;)