Gherkin & Cucumber in Java (using IntelliJ)

This post will be based around implementing features etc. using IntelliJ, so your experience with tooling may differ from those experiences outlined in this post.

If you’re coming from Specflow and C# to IntelliJ and Java

I’ve used Gherkin previously when using Specflow in Visual Studio and with C#. The concepts, syntax etc. are exactly the same, however be aware that the generated code isn’t just different in terms of the language used (i.e. C# and Java for example) but also in the RegEx generated. As an example if your Gherkin has numbers then Specflow generated code allows for negatives, whereas the code generated within the IntelliJ/Java systems is a little stricter with it’s RegEx and negatives need to be added to the RegEx.

Creating our demo project

  • File | New Project
  • Select Maven project type
  • Enter the GroupId and ArtifactId

Now open the pom.xml and add the following

<dependencies>
   <dependency>
      <groupId>info.cukes</groupId>
      <artifactId>cucumber-java</artifactId>
      <version>1.2.5</version>
      <scope>test</scope>
   </dependency>
   <dependency>
      <groupId>info.cukes</groupId>
      <artifactId>cucumber-jvm</artifactId>
      <version>1.2.5</version>
      <type>pom</type>
   </dependency>
   <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.12</version>
      <scope>test</scope>
   </dependency>
</dependencies>

Creating our code

Let’s create a simple little class to run our tests against. Within src/java add a new class, mine’s named com.putridparrot.Calculator. Here’s the code that’s stored in src/main/java/com.putridparrot package

package com.putridparrot;

public class Calculator {
    public int add(int a, int b) {
        return a + b;
    }
}

In src/test create a folder named resources and right mouse click on this folder and select Mark Directory As | Test Resources Root.

Note: the resources folder is not a requirement, more a convention.

Next, add a folder named features and within this folder create a new file named add.feature, this is a really simple example feature

Note: this post is not about writing good feature’s so this example is extremely basic.

Feature: Calculator
  
  Scenario Outline: add two numbers
    Given Two input values, <first> and <second>
    When I add the two values
    Then I expect the result <result>

  Examples:
    | first | second | result |
    | 1     | 12     | 13     |
    | -1    | 6      | 5      |

Select the end of each the Given, When and Then line and press alt+enter for each and select create steps definition. Name the file AddStepDef (mine’s created in the src/test/java/com/putridparrot folder/package) and set the file type to Java when you create your first step definition, then subsequently add step definitions to this same file.

Note: At the time of writing, selecting create all step definitions, does not seem to work as expected and simply creates a single step, not yet sure why.

Here’s what my generated steps looked like

package com.putridparrot;

import cucumber.api.PendingException;
import cucumber.api.java.en.Given;
import cucumber.api.java.en.Then;
import cucumber.api.java.en.When;

public class AddStepDefs {
    @Given("^Two input values, <first> and <second>$")
    public void twoInputValuesFirstAndSecond() throws Throwable {
        // Write code here that turns the phrase above into concrete actions
        throw new PendingException();
    }

    @When("^I add the two values$")
    public void iAddTheTwoValues() throws Throwable {
        // Write code here that turns the phrase above into concrete actions
        throw new PendingException();
    }

    @Then("^I expect the result <result>$")
    public void iExpectTheResultResult() throws Throwable {
        // Write code here that turns the phrase above into concrete actions
        throw new PendingException();
    }
}

Sadly these require editing as the placeholders (the <> wrapped text) are not being passed as arguments into our methods. The simplest way to solve this is to change the placeholders to numbers (or the type we expect) delete the methods from the step definition file and then regenerate the step definitions. So here’s my “temporary” change to the feature to get valid steps generated.

Feature: Calculator

  Scenario Outline: add two numbers
    Given Two input values, 1 and 2
    When I add the two values
    Then I expect the result 3

    Examples:
      | first | second | result |
      | 1     | 12     | 13     |
      | -1    | 6      | 5      |

this gives us the following code

package com.putridparrot;

import cucumber.api.PendingException;
import cucumber.api.java.en.Given;
import cucumber.api.java.en.Then;
import cucumber.api.java.en.When;

public class AddStepDefs {


    @Given("^Two input values, (\\d+) and (\\d+)$")
    public void twoInputValuesAnd(int arg0, int arg1) throws Throwable {
        // Write code here that turns the phrase above into concrete actions
        throw new PendingException();
    }

    @When("^I add the two values$")
    public void iAddTheTwoValues() throws Throwable {
        // Write code here that turns the phrase above into concrete actions
        throw new PendingException();
    }
    
    @Then("^I expect the result (\\d+)$")
    public void iExpectTheResult(int arg0) throws Throwable {
        // Write code here that turns the phrase above into concrete actions
        throw new PendingException();
    }

}

Return the feature file back to the version with placeholders.

Before we write our actual test code, let’s run the feature tests. In IntelliJ we can right mouse click on an individual feature file or the folder with the features and select Run feature or Run all features.

As we only have one feature file at the moment, select it, right mouse click and select Run: Feature Calculator, you’ll probably be hit with output in the test window along the lines of

Undefined step: Given… etc.

We need to edit the configuration for the runner and supply the Glue (in my case I have the steps in the package com.putridparrot, so I enter that in the Glue: option). Now running the feature should result in another error

Undefined step: Given Two input values, -1 and 6

As you can see, there’s a problem with the generated code as it expects a method that handles a negative number and none exist, we need to amend our RegEx for all methods to include a possible – sign. Changed all (\\d+) to (-?\\d+) now running the feature will have fixed the error.

Writing our test code

We can now write fairly standard unit testing code into the step definition file (in this case I’m using JUnit), so here’s a simple example

package com.putridparrot;

import cucumber.api.java.Before;
import cucumber.api.java.en.Given;
import cucumber.api.java.en.Then;
import cucumber.api.java.en.When;
import org.junit.Assert;

public class AddStepDefs {

    private Calculator calculator;
    private int value1;
    private int value2;
    private int result;

    @Before
    public void before() {
        calculator = new Calculator();
    }

    @Given("^Two input values, (-?\\d+) and (-?\\d+)$")
    public void twoInputValuesAnd(int arg0, int arg1) throws Throwable {
        value1 = arg0;
        value2 = arg1;
    }

    @When("^I add the two values$")
    public void iAddTheTwoValues() throws Throwable {
        result = calculator.add(value1, value2);
    }

    @Then("^I expect the result (-?\\d+)$")
    public void iExpectTheResult(int arg0) throws Throwable {
        Assert.assertEquals(arg0, result);
    }
}

Common mistakes

Glue, glue and more glue – make sure you set up the glue.