Cucumber Data Table – Convert a Three Column Table to a List

Introduction

Here in this post you will see an example on Cucumber data table – convert a three column table to a list. Cucumber is a tool for running automated acceptance tests written in a behavior-driven development (BDD) style. Cucumber is written in the Ruby programming language. Cucumber projects are available for other platforms beyond Ruby.

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/. The language that Cucumber understands is called Gherkin.

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.

Related Post:

Prerequisites

Java at least 1.8, Cucumber 1.2.5/6.10.3, Junit 4.12/5.8.0-M1, Maven 3.6.3

In this example I will show you how you can use cucumber’s nice feature that helps us to use tables in our scenarios. The table can easily be converted to a list or map that you can use in your step.

Project Setup

Create a maven based project in your favorite tool or IDE. The name of the project is java-cucumber-three-column-data-table-to-list.

If you want to use maven based project then you can use below pom file:

<?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>com.roytuts</groupId>
	<artifactId>java-cucumber-three-column-data-table-to-list</artifactId>
	<version>0.0.1-SNAPSHOT</version>

	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<maven.compiler.source>12</maven.compiler.source>
		<maven.compiler.target>12</maven.compiler.target>
	</properties>

	<dependencies>
		<dependency>
			<groupId>io.cucumber</groupId>
			<artifactId>cucumber-java</artifactId>
			<version>6.10.3</version>
		</dependency>

		<dependency>
			<groupId>io.cucumber</groupId>
			<artifactId>cucumber-junit</artifactId>
			<version>6.10.3</version>
			<scope>test</scope>
		</dependency>

		<dependency>
			<groupId>org.junit.jupiter</groupId>
			<artifactId>junit-jupiter-engine</artifactId>
			<version>5.8.0-M1</version>
			<scope>test</scope>
		</dependency>

		<dependency>
			<groupId>org.junit.vintage</groupId>
			<artifactId>junit-vintage-engine</artifactId>
			<version>5.8.0-M1</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>
			</plugin>
		</plugins>
	</build>
</project>

Test Resources

Create src/test/resources folder, if it already does not exist, for putting the resource files.

Do right-click on the project and go New -> Source Folder. Give Folder name: as src/test/resources and click on Finish button.

Feature File

Create a feature files called bonuslist.feature under src/test/resources/cuke/flow/feature with the below content:

Feature: An organization pays bonus to its employees due to outstanding profit
"""
The organization also calculates how much extra money it has to pay
to its employees in the financial year
"""
Scenario: An organization pays bonus based on designations
    Given an organization has 100000 employees
    When it pays bonus according to the designation
        | designation | bonus |  noOfEmployees  |
        |      HR     |  1000 |       100       |
        |      PM     | 50000 |      5000       |
        |   Sr. Dev   | 25000 |     15000       |
        |   Jr. Dev   | 15000 |     79900       |
    Then the organization has to pay total extra Rs. 1823600000 to 100000 employees

Test Runner Class

Create a cucumber test runner class as shown below:

package com.roytuts.cuke.flow;

import org.junit.runner.RunWith;

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

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

@RunWith(Cucumber.class)
@CucumberOptions(features = {
		"classpath:cuke/flow/feature/bonuslist.feature" }, glue = "com.roytuts.cuke.flow.steps", monochrome = true, plugin = {
				"pretty", "html:target/cucumber", "json:target/Cucumber.json", "junit:target/Cucumber.xml" })
public class CukeBonusListRunner {

}

In the above class we have configured below features using @CucumberOptions:

  • features – location of the feature file
  • glue – the package where the step definition class will be written
  • monochrome – we want the output in console in human readable format
  • plugin – in what format and where we want the generated output file

Running the Runner Class

Once we run the above class the following steps are generated in the console:

Feature: An organization pays bonus to its employees due to outstanding profit
  """
  The organization also calculates how much extra money it has to pay
  to its employees in the financial year
  """
  Scenario: An organization pays bonus based on designations                        # cuke/flow/feature/bonuslist.feature:8
    Given an organization has 100000 employees
    When it pays bonus according to the designation
    Then the organization has to pay total extra Rs. 1823600000 to 100000 employees
1 Scenarios (1 undefined)
3 Steps (3 undefined)
0m0.000s
You can implement missing steps with the snippets below:
@Given("^an organization has (\\d+) employees$")
public void an_organization_has_employees(int arg1) throws Throwable {
    // Write code here that turns the phrase above into concrete actions
    throw new PendingException();
}
@When("^it pays bonus according to the designation$")
public void it_pays_bonus_according_to_the_designation(DataTable arg1) throws Throwable {
    // Write code here that turns the phrase above into concrete actions
    // For automatic transformation, change DataTable to one of
    // List<YourType>, List<List<E>>, List<Map<K,V>> or Map<K,V>.
    // E,K,V must be a scalar (String, Integer, Date, enum etc)
    throw new PendingException();
}
@Then("^the organization has to pay total extra Rs\\. (\\d+) to (\\d+) employees$")
public void the_organization_has_to_pay_total_extra_Rs_to_employees(int arg1, int arg2) throws Throwable {
    // Write code here that turns the phrase above into concrete actions
    throw new PendingException();
}

In the above class we see that the methods have been generated from the feature file and we also see that each of the method throws PendingException() because we have not yet implemented any step defined in feature file.

DTO or VO Class

Create below DTO or VO class to represent the data table in the feature file. For Cucumber version 6.x you need to have custom converter otherwise your application won’t be able to convert the DataTable values into appropriate list of objects.

Notice in the following class I have added a method for creating instance of EmpBonus class.

package com.roytuts.cuke.flow.vo;

import java.util.Map;

public class EmpBonus {

	private String designation;
	private Integer bonus;
	private Integer noOfEmployees;

	public String getDesignation() {
		return designation;
	}

	public void setDesignation(String designation) {
		this.designation = designation;
	}

	public Integer getBonus() {
		return bonus;
	}

	public void setBonus(Integer bonus) {
		this.bonus = bonus;
	}

	public Integer getNoOfEmployees() {
		return noOfEmployees;
	}

	public void setNoOfEmployees(Integer noOfEmployees) {
		this.noOfEmployees = noOfEmployees;
	}

	//Cucumber 6.10.3
	public static EmpBonus createEmpBonus(Map<String, String> entry) {
		EmpBonus empBonus = new EmpBonus();
		empBonus.setDesignation(entry.get("designation"));
		empBonus.setBonus(Integer.parseInt(entry.get("bonus")));
		empBonus.setNoOfEmployees(Integer.parseInt(entry.get("noOfEmployees")));
		return empBonus;
	}
	//Cucumber 6.10.3
}

In the above class, each column attribute of the data table is mapped to the Java attribute or field.

Step Definition Class

Create a step definition class that will hold all generated steps with implementations. Modify the generated steps according to your needs.

package com.roytuts.cuke.flow.steps;

import java.util.List;

//Cucumber 1.2.5
//import org.hamcrest.CoreMatchers;
//import org.junit.Assert;
//import com.roytuts.cuke.flow.vo.EmpBonus;
//import cucumber.api.java.en.Given;
//import cucumber.api.java.en.Then;
//import cucumber.api.java.en.When;
//Cucumber 1.2.5

import org.junit.jupiter.api.Assertions;

import com.roytuts.cuke.flow.vo.EmpBonus;

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

public class BonusListSteps {

	private int totalEmployee;
	private int totalBonusAmt;

	@Given("^an organization has (\\d+) employees$")
	public void an_organization_has_employees(int totalEmployee) throws Throwable {
		this.totalEmployee = totalEmployee;
	}

	@When("^it pays bonus according to the designation$")
	public void it_pays_bonus_according_to_the_designation(List<EmpBonus> empBonus) throws Throwable {
		for (EmpBonus empBonus2 : empBonus) {
			Integer noOfEmployee = empBonus2.getNoOfEmployees();
			Integer bonusAmt = empBonus2.getBonus();
			totalBonusAmt += noOfEmployee * bonusAmt;
		}
	}

	@Then("^the organization has to pay total extra Rs. (\\d+) to (\\d+) employees$")
	public void the_organization_has_to_pay_total_extra_Rs_to_employees(int expTotalMoney, int expTotalEmployee)
			throws Throwable {
		// Assert.assertThat(totalBonusAmt, CoreMatchers.is(expTotalMoney)); //Junit 4
		// Assert.assertThat(totalEmployee, CoreMatchers.is(expTotalEmployee)); //Junit 4
		Assertions.assertEquals(totalBonusAmt, expTotalMoney);
		Assertions.assertEquals(totalEmployee, expTotalEmployee);
	}

}

Now notice we have implemented all steps we have declared in feature file and now we should not get any exception while we execute the Runner class.

Note: The parameter DataTable in step @Given can be converted to any one of List<YourType>, List<List<E>>, List<Map<K,V>> or Map<K,V>, where E, K, V must be scalar, i.e., String, Integer, Date, enum, Double etc.

In the above class the method it_pays_bonus_according_to_the_designation() will throw the following exception:

io.cucumber.core.exception.CucumberException: Could not convert arguments for step [^it pays bonus according to the designation$] defined at 'com.roytuts.cuke.flow.steps.BonusListSteps.it_pays_bonus_according_to_the_designation(java.util.List<com.roytuts.cuke.flow.vo.EmpBonus>)'.
It appears you did not register a data table type.

Or

io.cucumber.datatable.UndefinedDataTableTypeException: Can't convert DataTable to List<com.roytuts.cuke.flow.vo.EmpBonus>.
Please review these problems:

 - There was no table entry or table row transformer registered for com.roytuts.cuke.flow.vo.EmpBonus.
   Please consider registering a table entry or row transformer.

 - There was no default table entry transformer registered to transform com.roytuts.cuke.flow.vo.EmpBonus.
   Please consider registering a default table entry transformer.

Now create the following class that will convert your DataTable to a list of EmpBonus objects. Note that the interface TypeRegistryConfigurer has been deprecated but I did not find alternative now.

package com.roytuts.cuke.flow.steps;

import java.util.Locale;
import java.util.Map;

import com.roytuts.cuke.flow.vo.EmpBonus;

import io.cucumber.core.api.TypeRegistry;
import io.cucumber.core.api.TypeRegistryConfigurer;
import io.cucumber.datatable.DataTableType;
import io.cucumber.datatable.TableEntryTransformer;

public class Configurer implements TypeRegistryConfigurer {
	@Override
	public void configureTypeRegistry(TypeRegistry registry) {
		registry.defineDataTableType(new DataTableType(EmpBonus.class, new TableEntryTransformer<EmpBonus>() {
			@Override
			public EmpBonus transform(Map<String, String> entry) {
				return EmpBonus.createEmpBonus(entry);
			}
		}));
	}

	@Override
	public Locale locale() {
		return Locale.ENGLISH;
	}

}

Important: You need to to put the above class in the same package where your step definition class is there.

Re-running the Runner Class

Execute the cucumber test runner class we created earlier. You will see the below output in the console.

Feature: An organization pays bonus to its employees due to outstanding profit
  """
  The organization also calculates how much extra money it has to pay
  to its employees in the financial year
  """
  Scenario: An organization pays bonus based on designations                        # cuke/flow/feature/bonuslist.feature:8
    Given an organization has 100000 employees                                      # BonusListSteps.an_organization_has_employees(int)
    When it pays bonus according to the designation                                 # BonusListSteps.it_pays_bonus_according_to_the_designation(EmpBonus>)
    Then the organization has to pay total extra Rs. 1823600000 to 100000 employees # BonusListSteps.the_organization_has_to_pay_total_extra_Rs_to_employees(int,int)
1 Scenarios (1 passed)
3 Steps (3 passed)
0m0.371s

Now all the test cases are passed from the above output as we see.

Source Code

Download

1 thought on “Cucumber Data Table – Convert a Three Column Table to a List

Leave a Reply

Your email address will not be published. Required fields are marked *