<Tech>Brunch

Behaviour Driven Development in Java using Cucumber

Behaviour Driven Development(BDD) is a way for software teams to work that closes the gap between business people and technical people by:

  • Encouraging collaboration across roles to build shared understanding of the the problem to be solved
  • Working in rapid, small iterations to increase feedback and the flow of value
  • Producing system documentation that is automatically checked against the system’s behaviour

BDD is helpful in a work environment which is driven by Agile Methodology, where small user stories are being delivered in iterations. It enhances the delivery model, ensuring that timely, well tested code, and covering all the scenarios that bussiness expect to have from the user story. BDD also focuses on automating the process where business scenarios are well tested, and moves the manual effort involved in testing away as much as possible. Well, manual QA effort cannot be removed all together but BDD tries to minimize it.




There are several frameworks around us right now which supports BDD in your development. I tried to have my hands on ‘Cucumber’.
Cucumber is a software tool used by computer programmers that supports behavior-driven development. Central to the Cucumber BDD approach is its plain language parser called ‘Gherkin’. Gherkin language is a simple English text language hence anybody can understand it without any technical knowledge. It allows expected software behaviors to be specified in a logical language that customers can understand.
Cucumber supports a variety of different programming languages including Java, JavaScript, PHP, Net, Python, Perl, etc. with various implementations. In Java, it supports native JUnit.

While writing test cases with Cucumber, key points that we need to keep in mind behind its implementation are:

  • Cucumber test cases are written parallel with the code development of software, called as ‘step’ in Gherkin.
  • Cucumber tool reads the step written in a Gherkin or plain English text inside the feature file.
  • Cucumber searches for the exact match of each step in the step definition file. When it finds its match, then executes the test case and provides the result as pass or fail.
  • The code of developed software must correspond with the BDD defined test scripts. If it does not, then code refactoring will be required. The code gets freeze only after successful execution of defined test scripts.

Let me give you a step-by-step walkthrough on how I implemented Cucumber in one of my maven projects:

  1. Add Cucumber related dependencies in your project pom.xml:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    <!-- dependency addition for Cucumber Starts -->
    <dependency>
    <groupId>io.cucumber</groupId>
    <artifactId>cucumber-java</artifactId>
    <version>2.3.1</version>
    <scope>test</scope>
    </dependency>
    <dependency>
    <groupId>io.cucumber</groupId>
    <artifactId>cucumber-junit</artifactId>
    <version>2.3.1</version>
    <scope>test</scope>
    </dependency>
    <!-- dependency addition for Cucumber ends -->
  2. Install Cucumber plugin in your IDE. This plugin is available for Eclipse, as well as for IntelliJ.

  3. Navigate to src/test/resources in your project and create a new folder there where in you will be keeping your feature files. I created a folder called ‘feature-files’ inside resources folder of src/test. I pasted the demo feature file there, named ‘demo.feature’.
  4. My feature file content goes like below:

    1
    2
    3
    4
    5
    6
    Feature: Demo feature

    Scenario: Demonstrating how Cucumber-JVM works
    Given I have a configured Cucumber-JVM project
    When I run it within my IDE
    Then I will be able to run connected step definitions
  5. Right click on the feature file, and Run as Cucumber. The console will show the step definitons generated by Cucumber. We can copy this code and keep it a step definition file dedicated for this. In my case I created DemoStepDefinitions, and pasted all the code generated by Cucumber in console.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    package com.techbrunch.cucumber.step.definitions;

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

    public class DemoStepDefinitions {

    @Given("^I have a configured Cucumber-JVM project$")
    public void i_have_a_configured_Cucumber_JVM_project() throws Exception {
    System.out.println("Inside i_have_a_configured_Cucumber_JVM_project");
    //throw new PendingException();
    }

    @When("^I run it within my IDE$")
    public void i_run_it_within_my_IDE() throws Exception {
    System.out.println("Inside i_run_it_within_my_IDE");
    //throw new PendingException();
    }

    @Then("^I will be able to run connected step definitions$")
    public void i_will_be_able_to_run_connected_step_definitions() throws Exception {
    System.out.println("Inside i_will_be_able_to_run_connected_step_definitions");
    throw new PendingException();
    }

    }
  6. We can update the code in the above class to meet our business scenario. I have only provided some sysos there. But the actual testing code will go here, using JUnit/Mocito/PowerMock.

  7. I created a Cucmber test runner class, CucumberTestRunner, which I can make use of to trigger all the step definiton java files. In my case I have here only one, DemoStepDefinitions, and in my Test Runner class I will mention to make use of DemoStepDefinitions step definiton class.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    package com.techbrunch.cucumber.test.runner;

    import org.junit.runner.RunWith;

    import cucumber.api.CucumberOptions;
    import cucumber.api.junit.Cucumber;

    @RunWith(Cucumber.class)
    @CucumberOptions(features = { "src/test/resources/feature-files/demo.feature" }, glue = {
    "com.techbrunch.cucumber.step.definitions" }, plugin = { "pretty", "html:reports" })
    public class CucumberTestRunner {

    }
  8. @CucumberOptions annotation: This annotation provides some important information which will be used to run your cucumber feature file. At the very least, Java should know the location of the feature file as well as the step definition class in your project. CucumberOptions annotation will do just that. As of now, we have provided two parameters in this annotation.
    The first parameter, called features, provides the location of the feature file. Similarly, the second parameter, called glue, provides the path of the step definition package.
    I have made use of plugin parameter, where I am supplying values to make output pretty, and asking Cucumber to generate a report also for me. The report will be generated in reports folder.

  9. We can run the CucumberTestRunner class by Right click -> Run as JUnit.
  10. Console output is below:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    Feature: Demo feature

    Scenario: Demonstrating how Cucumber-JVM works # src/test/resources/feature-files/demo.feature:3
    Inside i_have_a_configured_Cucumber_JVM_project
    Given I have a configured Cucumber-JVM project # DemoStepDefinitions.i_have_a_configured_Cucumber_JVM_project()
    Inside i_run_it_within_my_IDE
    When I run it within my IDE # DemoStepDefinitions.i_run_it_within_my_IDE()
    Inside i_will_be_able_to_run_connected_step_definitions
    Then I will be able to run connected step definitions # DemoStepDefinitions.i_will_be_able_to_run_connected_step_definitions()
    cucumber.api.PendingException: TODO: implement me
    at com.techbrunch.cucumber.step.definitions.DemoStepDefinitions.i_will_be_able_to_run_connected_step_definitions(DemoStepDefinitions.java:25)
    at ✽.I will be able to run connected step definitions(src/test/resources/feature-files/demo.feature:6)


    1 Scenarios (1 pending)
    3 Steps (1 pending, 2 passed)
    0m0.406s

    cucumber.api.PendingException: TODO: implement me
    at com.techbrunch.cucumber.step.definitions.DemoStepDefinitions.i_will_be_able_to_run_connected_step_definitions(DemoStepDefinitions.java:25)
    at ✽.I will be able to run connected step definitions(src/test/resources/feature-files/demo.feature:6)
  1. Above is the screenshot of the html report generated by Cucumber for us.