Press "Enter" to skip to content

Using Cucumber in Java unit testing

While working in Ruby at my previous workplace we were required to write unit tests using RSpec and end-to-end tests using Cucumber and Watir webdriver. I wasn’t a big fan nor am I today of writing tests, but for some reason I just loved Cucumber. We wrote them together with the members of the QA and it was fun, because we both were thinking about all the possible cases to test and not about the way the tests were going to be implemented. Furthermore, it was clear for everybody what I wanted to test, as the scenarios were written in natural language.

BDD in Java with Cucumber

Now, at the company I currently work for I had to rewrite a component, because the old implementation was hard to understand, contained too much little hacks and it became highly unflexible. But this wasn’t easy, because I didn’t have any documentation and there were quite a lot of flows which I did not think of. A set of feature files would have come handy, so I could see all the possible flows. I have suggested for our architect that it would be nice to use Cukes for our unit tests and although I don’t really think we will be allowed to use them, he allowed me to play with Cucumber for a couple of days just to see how it could be used in our project.

It wasn’t too hard… I could very easily integrate it in a Java project that is based on Maven.

1. The piece of code I want to test

I have created a very simple Person class in Java that contains a minimal amount of logic I want to test with Cucumber and JUnit.

package net.gazsi.laszlo.sandbox;

public class Person {

    private String name;

    public Person(String name) {
        this.name = name;
    }

    public String wakeUp(){
        return this.name + " wakes up...";
    }

    public String goToWork(boolean isWeekend){
        final String result;
        if (isWeekend){
            result = this.name + " does not go to work!";
        }
        else{
            result = this.name + " goes to work!";
        }

        return result;
    }
}

2. Importing the necessary dependencies

You need to add a few dependencies in your pom.xml to import JUnit and Cucumber.

<?xml version="1.0" encoding="UTF-8"?>  
<project xmlns="http://maven.apache.org/POM/4.0.0"  
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>net.gazsi.laszlo.sandbox</groupId>
    <artifactId>cucumber-demo</artifactId>
    <version>1.0</version>
    <packaging>war</packaging>

    <dependencies>
        <dependency>
            <groupId>info.cukes</groupId>
            <artifactId>cucumber-picocontainer</artifactId>
            <version>1.1.5</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>info.cukes</groupId>
            <artifactId>cucumber-junit</artifactId>
            <version>1.1.5</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.11</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

</project>  

3. Creating feature files

I have created the go_to_work.feature file in the test/resources/cukes directory:

Feature: During the week every person should go to work, but should not go in the weekends.

  Background:
    Given There is a person

  Scenario: It is Monday morning
    And it is not weekend
    When the alarm rings
    Then the person should get up and go to work

  Scenario: It is Saturday morning
    And it is weekend
    When the alarm rings
    Then the person should not get up and go to work

4. Implementing the necessary steps

Now we have to implement the steps defined in the feature file. Create one or more files in the test/java directory which will contain the definitions of the steps. I have created a single file for this example, but if you have a bunch of steps, it would be wise to separate them. My StepDefinitions class looks like this:

import cucumber.api.java.en.And;  
import cucumber.api.java.en.Given;  
import cucumber.api.java.en.Then;  
import cucumber.api.java.en.When;  
import net.gazsi.laszlo.sandbox.Person;  
import org.junit.Assert;

public class StepDefinitions {

    private static final String NAME = "Laszlo";
    private static final String NOT = "not";

    private Person person;
    private boolean isWeekend;

    @Given("^There is a person$")
    public void There_is_a_person() throws Throwable {
        this.person = new Person(NAME);
    }

    @And("^it is (.*)weekend$")
    public void it_is_weekend(String isOrIsNotWeekend) throws Throwable {
        this.isWeekend = !(NOT.equals(isOrIsNotWeekend));
    }

    @When("^the alarm rings$")
    public void the_alarm_rings() throws Throwable {
        //TODO: make some irritating noise...
    }

    @Then("^the person should (.*)get up and go to work$")
    public void the_person_should_get_up_and_go_to_work(String isOrIsNotWeekend) throws Throwable {
        final String expectedMessage;
        if (NOT.equals(isOrIsNotWeekend)){
            expectedMessage = NAME + " goes to work!";
        }
        else {
            expectedMessage = NAME + " does not go to work!";
        }

        String actualMessage = this.person.goToWork(this.isWeekend);
        Assert.assertEquals(expectedMessage, actualMessage);
    }
}

5. Running the tests

We are almost ready. We only need a way to run the Cuke tests when running a Maven build. To do this, create a new empty Java class in the test/java folder. Mine looks like this:

import cucumber.api.CucumberOptions;  
import cucumber.api.junit.Cucumber;  
import org.junit.runner.RunWith;

@RunWith(Cucumber.class)
@CucumberOptions(monochrome = true, format = {"pretty", "html:target/cucumber", "rerun:target/rerun.txt"})
public class RunCukesTest {  
}

Using the RunWith annotation we specify that it should be run with Cucumber. The CucumberOptions annotation is used to set the options for running the Cuke tests.

At the next Maven build all features should be tested…