Cucumber Test Framework – Cucumber-JVM with Cucumber-Java + Cucumber-Junit

Introduction

Cucumber test framework is a tool for running automated acceptance tests written in a behavior-driven development (BDD) style. Cucumber test framework is written in the Ruby programming language. Cucumber projects are available for other platforms beyond Ruby. Some use Ruby Cucumber with a bridge into the target language (e.g. cuke4php and cuke4lua). Others use the Gherkin parser but implement everything else in the target language. Cucumber allows the execution of feature documentation written in business-facing text.

Cucumber works with Ruby, Java, .NET, Flex or web applications written in any language. It has been translated to over 40 spoken languages. – http://cukes.info/

Feature File Example

The language that Cucumber understands is called Gherkin. Here is an example: – https://github.com/cucumber/cucumber/wiki

Feature: Search courses

Courses should be searchable by topic
Search results should provide the course code

Scenario: Search by topic

Given there are 240 courses which do not have the topic "biology"
And there are 2 courses A001, B205 that each have "biology" as one of the topics

When I search for "biology"

Then I should see the following courses:
| Course code |
| A001        |
| B205        |

While Cucumber can be thought of as a “testing” tool, the intent of the tool is to support BDD. This means that the “tests” (plain text feature descriptions with scenarios) are typically written before anything else and verified by business analysts, domain experts, etc. non technical stakeholders. The production code is then written outside-in, to make the stories pass.

Cucumber itself is written in Ruby, but it can be used to “test” code written in Ruby or other languages including but not limited to Java, C# and Python.

I will show an example how cucumber test framework – cucumber-jvm fits with cucumber-java and cucumber-junit.

You may also like to read:

Prerequisites

Java at least 8, Cucumber Framework 6.8.1, Gradle 6.5.1, Maven 3.6.3

Project Setup

You can create either gradle based or maven based project in your favorite IDE or tool. The name of the project is cucumber-java-junit.

If you are creating maven based project then open the pom.xml file and add the following dependencies for cucumber test framework and junit.

<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>com.roytuts</groupId>
	<artifactId>cucumber-java-junit</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>jar</packaging>
	
	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<java.version>at least 1.8</java.version>
	</properties>
	
	<dependencies>
		<dependency>
			<groupId>io.cucumber</groupId>
			<artifactId>cucumber-java</artifactId>
			<version>6.8.1</version>
			<scope>test</scope>
		</dependency>
		
		<dependency>
			<groupId>io.cucumber</groupId>
			<artifactId>cucumber-junit</artifactId>
			<version>6.8.1</version>
			<scope>test</scope>
		</dependency>
		
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>4.13.1</version>
			<scope>test</scope>
		</dependency>
	</dependencies>
	
	<build>
		<plugins>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-compiler-plugin</artifactId>
				<version>3.8.1</version>
                <configuration>
					<source>${java.version}</source>
					<target>${java.version}</target>
				</configuration>
			</plugin>
		</plugins>
	</build>
</project>

If you are creating gradle based project then you can use the below build.gradle script for your project:

plugins {
    id 'java-library'
}

sourceCompatibility = 12
targetCompatibility = 12

repositories {
    jcenter()
}

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

Feature File

A standard Maven or Gradle project, the directory structure will have a test source folder src/test/resources, this is the directory where you should create your .feature files. So let’s create a feature for adding two numbers.

Create a file called addition.feature in src/test/resources. You can, of course, create packages in this source folder to organize your feature files. I have put the feature file under com.roytuts.cucumber.java.junit package. So the full path for the feature file is src/test/resources/com/roytuts/cucumber/java/junit/addition.feature.

Now let’s write first simple scenario. You want to test that when two numbers are added together.

Feature: Addition of two numbers

Scenario: Add two numbers

Given I input numbers 10 and 20 into the calculator
When I press add
Then I should get the result 30 on the screen

The above first scenario describes the steps that will have to be executed successfully for the Scenario to pass.

We will run the .feature using a JUnit Runner, which you are having from the Cucumber-Junit Library.

Java Test Class

So all you need to do is to create a blank test class, and annotate it with JUnit’s @RunWith annotation as follows:

package com.roytuts.cucumber.java.junit;

import org.junit.runner.RunWith;

import io.cucumber.junit.Cucumber;
import io.cucumber.junit.CucumberOptions;

@RunWith(Cucumber.class)
@CucumberOptions(plugin = { "pretty", "html:target/addition.json" })
public class TestAddition {

}

You can now run TestAddition.java like any other JUnit test class.

Running the Test Class

If you run the above test class you will see the following output in the console:

Scenario: Add two numbers                             # com/roytuts/cucumber/java/junit/addition.feature:3
  Given I input numbers 10 and 20 into the calculator # null
  When I press add                                    # null
  Then I should get the result 30 on the screen       # null

You will get below output in the Junit trace:

@Given("I input numbers {int} and {int} into the calculator")
public void i_input_numbers_and_into_the_calculator(Integer int1, Integer int2) {
    // Write code here that turns the phrase above into concrete actions
    throw new io.cucumber.java.PendingException();
}

@When("I press add")
public void i_press_add() {
    // Write code here that turns the phrase above into concrete actions
    throw new io.cucumber.java.PendingException();
}

@Then("I should get the result {int} on the screen")
public void i_should_get_the_result_on_the_screen(Integer int1) {
    // Write code here that turns the phrase above into concrete actions
    throw new io.cucumber.java.PendingException();
}

Take a look at the JUnit trace, the scenario steps were actually skipped, they were not executed because no step definitions were found. You’ll see some very helpful information about the scenario that was executed and the steps which were skipped.

The output also has shown you default implementations for the step definitions, how nice is that!

Step Definition Class

Let’s create a class for the step definitions. At this point if you run the below class you will see each step throwing PendingException() because the implementation for each step is still pending.

package com.roytuts.cucumber.java.junit;

import io.cucumber.java.en.Given;
import io.cucumber.java.en.Then;
import io.cucumber.java.en.When;

public class AdditionStepDefinition {

    @Given("I input numbers {int} and {int} into the calculator")
    public void i_input_numbers_and_into_the_calculator(Integer int1, Integer int2) {
        throw new io.cucumber.java.PendingException();
    }

    @When("I press add")
    public void i_press_add() {
        throw new io.cucumber.java.PendingException();
    }

    @Then("I should get the result {int} on the screen")
    public void i_should_get_the_result_on_the_screen(Integer int1) {
        throw new io.cucumber.java.PendingException();
    }

}

At this point you are ready to implement the step definitions for cucumber test framework. Let’s start with the steps.

package com.roytuts.cucumber.java.junit;

import static org.junit.Assert.assertTrue;

import io.cucumber.java.en.Given;
import io.cucumber.java.en.Then;
import io.cucumber.java.en.When;

public class AdditionStepDefinition {

    private int num1;
    private int num2;
    private int total;

    @Given("I input numbers {int} and {int} into the calculator")
    public void i_input_numbers_and_into_the_calculator(Integer int1, Integer int2) {
        num1 = int1;
        num2 = int2;

        assertTrue("The total is not zero.", total == 0);
    }

    @When("I press add")
    public void i_press_add() {
        total = num1 + num2;
    }

    @Then("I should get the result {int} on the screen")
    public void i_should_get_the_result_on_the_screen(Integer int1) {
        assertTrue("The expected total was 30, but actually is: " + total, total == int1);
    }

}

Re-running the Test Class

Now run AdditionTest.java and you will see that all test steps have been passed.

java junit cucumber test framework

The AdditionStepDefinition class could be refactored because num1, num2 etc. variables should not be declared here and it’s just for testing purpose.

Source Code

Download

Leave a Comment