Hot questions for Using Cucumber in datatable

Question:

I updated from Cucumber-JVM 2.4.0 to 3.0.2 in my pom.xml and DataTables started throwing this exception:

io.cucumber.datatable.UndefinedDataTableTypeException: Can't convert DataTable to List< jcucumberng.steps.pojos.Income >. Please register a DataTableType with a TableEntryTransformer or TableRowTransformer for class jcucumberng.steps.pojos.Income

I changed all my imports to

import io.cucumber.datatable.DataTable;

I did an mvn clean install and compilation was successful but steps involving DataTables no longer work after the update.

Current code:

// Feature
When I Enter My Regular Income Sources
  | name   | amount | frequency     |
  | Salary | 25000  | every 2 weeks |


// Stepdef
@When("^I Enter My Regular Income Sources$")
public void I_Enter_My_Regular_Income_Sources(DataTable dataTable) throws Throwable {
    List<Income> incomes = dataTable.asList(Income.class);

    // More code    
}


// Custom type
public class Income {

    private String name = null;
    private String amount = null;
    private String frequency = null;

    public Income(String name, String amount, String frequency) {
        this.name = name;
        this.amount = amount;
        this.frequency = frequency;
    }

    // Getters and setters
}

Is there a new way to use the DataTables in Cucumber-JVM v3.x.x?

UPDATE:


Answer:

It has been totally revamped. XStream has been removed , so earlier code will not work.

You will need to add logic for datatable and parameter conversion. Refer to these - https://github.com/cucumber/cucumber/tree/master/datatable and https://github.com/cucumber/cucumber/tree/master/cucumber-expressions . Place below class code inside a package defined in the glue option.

public class Configurer implements TypeRegistryConfigurer {

    @Override
            public void configureTypeRegistry(TypeRegistry registry) {

    registry.defineDataTableType(new DataTableType(Income.class, new TableEntryTransformer<Income>() {
                    @Override
                    public Income transform(Map<String, String> entry) {
                        return new Income(entry.get("name"),entry.get("amount"),entry.get("frequency"));
                    }
                }));
            }

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

        }

UPDATED Imports... Not all are required, keep what is relevant

import cucumber.api.TypeRegistry;
import cucumber.api.TypeRegistryConfigurer;
import io.cucumber.cucumberexpressions.ParameterType;
import io.cucumber.datatable.DataTable;
import io.cucumber.datatable.DataTableType;
import io.cucumber.datatable.TableCellTransformer;
import io.cucumber.datatable.TableEntryTransformer;
import io.cucumber.datatable.TableRowTransformer;
import io.cucumber.datatable.TableTransformer;

Question:

I want to know if there is any way to convert a HashMap to a DataTable in java Cucumber. I tried doing some googling and saw that the reverse is possible. Any idea on how to implement this?

Thanks in advance.


Answer:

Considering that

  1. All the data is kept in the map as Strings and
  2. You want a single row in your data table, wherein headers would be keys and row items would be values from the input hashmap. If the conversion would have been other way around, then you would have a list of Maps as Data table would have multiple rows (values for different maps) against the columns (keys).

You could try:

List<List<String>> data = Arrays.asList(new ArrayList<String>(map.keySet()), new ArrayList<String>(map.values())); 
DataTable dataTable = DataTable.create(data);

P.S. I have not tested the syntax.

Question:

Trying to run a scenario with cucumber/selenium/java/intelliJ, but getting an error regarding the DataTable in one of the steps. The dataTable was working fine and converting the arguments for the step correctly before I started using a test runner and changed some things around, but I just can't get this to work.

This is the error:

cucumber.runtime.CucumberException: Could not convert arguments for step [^I enter the following login details:$] defined at 'Steps.MaStepdefs.iEnterTheFollowingLoginDetails(DataTable) in file:/C:/Users/Kristian.Senior/Desktop/CukesReporting/target/test-classes/'.
It appears you did not register a data table type. The details are in the stacktrace below.
    at cucumber.runner.PickleStepDefinitionMatch.registerTypeInConfiguration(PickleStepDefinitionMatch.java:59)
    at cucumber.runner.PickleStepDefinitionMatch.runStep(PickleStepDefinitionMatch.java:44)
    at cucumber.runner.TestStep.executeStep(TestStep.java:63)
    at cucumber.runner.TestStep.run(TestStep.java:49)
    at cucumber.runner.PickleStepTestStep.run(PickleStepTestStep.java:43)
    at cucumber.runner.TestCase.run(TestCase.java:45)
    at cucumber.runner.Runner.runPickle(Runner.java:40)
    at cucumber.runtime.junit.PickleRunners$NoStepDescriptions.run(PickleRunners.java:146)
    at cucumber.runtime.junit.FeatureRunner.runChild(FeatureRunner.java:68)
    at cucumber.runtime.junit.FeatureRunner.runChild(FeatureRunner.java:23)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:309)
    at cucumber.runtime.junit.FeatureRunner.run(FeatureRunner.java:73)
    at cucumber.api.junit.Cucumber.runChild(Cucumber.java:122)
    at cucumber.api.junit.Cucumber.runChild(Cucumber.java:64)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229)
    at cucumber.api.junit.Cucumber$1.evaluate(Cucumber.java:131)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:309)
    at org.junit.runner.JUnitCore.run(JUnitCore.java:160)
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
    at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
    at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
    at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
Caused by: io.cucumber.datatable.UndefinedDataTableTypeException: Can't convert DataTable to cucumber.api.DataTable.
Please register a DataTableType with a TableTransformer, TableEntryTransformer or TableRowTransformer for cucumber.api.DataTable.
    at io.cucumber.datatable.UndefinedDataTableTypeException.singletonNoConverterDefined(UndefinedDataTableTypeException.java:15)
    at io.cucumber.datatable.DataTableTypeRegistryTableConverter.toSingleton(DataTableTypeRegistryTableConverter.java:106)
    at io.cucumber.datatable.DataTableTypeRegistryTableConverter.convert(DataTableTypeRegistryTableConverter.java:75)
    at io.cucumber.datatable.DataTable.convert(DataTable.java:362)
    at io.cucumber.stepexpression.StepExpressionFactory$3.transform(StepExpressionFactory.java:73)
    at io.cucumber.stepexpression.DataTableArgument.getValue(DataTableArgument.java:19)
    at cucumber.runner.PickleStepDefinitionMatch.runStep(PickleStepDefinitionMatch.java:41)
    ... 29 more

Here is my scenario:

Feature: LoginFeature
  This deals with logging in

  Scenario: Log in with correct username

    Given I navigate to the login page
    And I enter the following login details:
      | username | password |
      | cukey    | passwoid |
    And I click the login button
    Then I should land on the newest page

Here's my step definitions:

package Steps;

import Base.BaseUtil;
import Pages.LoginPageObjeks;
import cucumber.api.DataTable;
import cucumber.api.PendingException;
import cucumber.api.java.en.And;
import cucumber.api.java.en.Given;
import cucumber.api.java.en.Then;
import io.github.bonigarcia.wdm.WebDriverManager;
import org.junit.Assert;
import org.openqa.selenium.By;
import org.openqa.selenium.chrome.ChromeDriver;

import java.util.ArrayList;
import java.util.List;


public class MaStepdefs extends BaseUtil {
    private BaseUtil base;

    public MaStepdefs(BaseUtil base) {
        this.base = base;
    }

    @And("^I click the login button$")
    public void iClickTheLoginButton() throws Throwable {
        LoginPageObjeks page = new LoginPageObjeks(base.Driver);
        page.ClickLogin();

    }

    @Given("^I navigate to the login page$")
    public void iNavigateToTheLoginPage() throws Throwable {

        base.Driver.navigate().to("http://www.executeautomation.com/demosite/Login.html");

    }

    @And("^I enter the following login details:$")
    public void iEnterTheFollowingLoginDetails(DataTable table) throws Throwable {

        List<User> users = new ArrayList<User>();

        users = table.asList(User.class);

        LoginPageObjeks page = new LoginPageObjeks(base.Driver);

        for (User user : users) {
            page.Login(user.username, user.password);

            //base.Driver.findElement(By.name("UserName")).sendKeys(user.username);
            //base.Driver.findElement(By.name("Password")).sendKeys(user.password);

            Thread.sleep(2000);


        }
    }
        @Then("^I should land on the newest page$")
        public void iShouldLandOnTheNewestPage () throws Throwable {
            Assert.assertEquals("It's not displayed", base.Driver.findElement(By.id("Initial")).isDisplayed(), true);
        }



    }

    class User {
        public String username;
        public String password;

        public User(String userName, String passWord) {
            username = userName;
            password = passWord;
        }
    }

Here is my pom:

<?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>groupId</groupId>
    <artifactId>ownCukes</artifactId>
    <version>1.0-SNAPSHOT</version>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.5.1</version>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>2.19</version>
            </plugin>
        </plugins>
    </build>

    <dependencies>

        <!-- https://mvnrepository.com/artifact/io.cucumber/cucumber-java -->
        <dependency>
            <groupId>io.cucumber</groupId>
            <artifactId>cucumber-java</artifactId>
            <version>4.2.6</version>
        </dependency>

        <dependency>
            <groupId>org.seleniumhq.selenium</groupId>
            <artifactId>selenium-java</artifactId>
            <version>3.141.59</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.11</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>io.github.bonigarcia</groupId>
            <artifactId>webdrivermanager</artifactId>
            <version>3.2.0</version>
            <scope>test</scope>
        </dependency>

        <!-- https://mvnrepository.com/artifact/io.cucumber/cucumber-picocontainer -->
        <dependency>
            <groupId>io.cucumber</groupId>
            <artifactId>cucumber-picocontainer</artifactId>
            <version>4.2.6</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>io.cucumber</groupId>
            <artifactId>cucumber-junit</artifactId>
            <version>4.2.6</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>io.cucumber</groupId>
            <artifactId>cucumber-junit</artifactId>
            <version>4.2.6</version>
            <scope>test</scope>
        </dependency>
        <!-- https://mvnrepository.com/artifact/io.cucumber/cucumber-core -->

        <dependency>
            <groupId>info.cukes</groupId>
            <artifactId>cucumber-core</artifactId>
            <version>1.2.5</version>
            <scope>test</scope>
        </dependency>


    </dependencies>


</project>

And here's my TestRunner:

package Runner;


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

@RunWith(Cucumber.class)
@CucumberOptions(features = {"src/test/java/features"}, glue = "Steps")

public class TestRunner {
}

I'm not sure what a data table type is or how to register one? I've tried messing around with DataTableType and TypeRegistry but I'm not an expert. Any help would be appreciated, thanks


Answer:

You are using outdated version, it is now:

import io.cucumber.datatable.DataTable;

See CHANGELOG for cucumber jvm, quote:

[Core] Replace DataTable with io.cucumber.datatable.DataTable (#1248 M.P. Korstanje, Björn Rasmusson, Marit van Dijk) Custom data table types can be defined by implementing the TypeRegistryConfigurer.

There is slightly different way of using it now, example:

Sample gherkin step:

And I update ADDRESS tab data in building form
  | Input         | Value        |
  | Building name | New name     |
  | Ref           | Some ref     |

Step implementation:

@And("^some test step$")
public void someTestStep(DataTable table)
{
    List<List<String>> data = table.asLists(String.class);
    String buildingName = data.get(1).get(1);
    String reference = data.get(2).get(1);
}

Question:

In cucumber-jvm 3.x.x, I want to convert a datatable with only 2 columns to a single object. This used to be automatic in 2.x.x but has since been removed with the new major version.

Code:

// Feature
Scenario: Scan Barcode
    Given I Am At The Login Page
    When I Log In As Valid User
        | group    | gpao     |
        | username | svgpao1  |
        | password | REDACTED |

// Stepdef
@When("I Log In As Valid User")
public void I_Log_In_As_Valid_User(User user) throws Throwable {
    Selenium.enterText(driver, user.getUsername(), "username");
    Selenium.enterText(driver, user.getPassword(), "password");
    Selenium.clickElement(driver, "sign.in");
    LOGGER.debug("User Group=" + user.getGroup());
}

// Configurer
registry.defineDataTableType(new DataTableType(User.class, new TableTransformer<User>() {
    @Override
    public User transform(DataTable dataTable) throws Throwable {
        Map<String, String> map = dataTable.asMap(String.class, String.class);
        return new User(map);
    }
}));

// User - constructor
public User(Map<String, String> map) {
    this.group = map.get("group");
    this.username = map.get("username");
    this.password = map.get("password");
}

Stacktrace:

cucumber.runtime.CucumberException: Could not convert arguments for step [I Log In As Valid User] defined at 'jcucumberng.project.stepdefs.EnterPacketDetailsSteps.I_Log_In_As_Valid_User(User) in file:/Z:/workspace/java/jcucumberng-framework/target/test-classes/'.
The details are in the stacktrace below.
        at cucumber.runtime.PickleStepDefinitionMatch.couldNotConvertArguments(PickleStepDefinitionMatch.java:69)
        at cucumber.runtime.PickleStepDefinitionMatch.runStep(PickleStepDefinitionMatch.java:46)
        at cucumber.runner.TestStep.executeStep(TestStep.java:55)
        at cucumber.runner.TestStep.run(TestStep.java:42)
        at cucumber.runner.PickleStepTestStep.run(PickleStepTestStep.java:53)
        at cucumber.runner.TestCase.run(TestCase.java:47)
        at cucumber.runner.Runner.runPickle(Runner.java:44)
        at cucumber.runtime.junit.PickleRunners$NoStepDescriptions.run(PickleRunners.java:140)
        at cucumber.runtime.junit.FeatureRunner.runChild(FeatureRunner.java:68)
        at cucumber.runtime.junit.FeatureRunner.runChild(FeatureRunner.java:23)
        at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
        at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
        at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
        at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
        at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
        at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
        at cucumber.runtime.junit.FeatureRunner.run(FeatureRunner.java:73)
        at cucumber.api.junit.Cucumber.runChild(Cucumber.java:98)
        at cucumber.api.junit.Cucumber.runChild(Cucumber.java:55)
        at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
        at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
        at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
        at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
        at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
        at cucumber.api.junit.Cucumber$1.evaluate(Cucumber.java:107)
        at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
        at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
        at org.apache.maven.surefire.junit4.JUnit4Provider.execute(JUnit4Provider.java:365)
        at org.apache.maven.surefire.junit4.JUnit4Provider.executeWithRerun(JUnit4Provider.java:273)
        at org.apache.maven.surefire.junit4.JUnit4Provider.executeTestSet(JUnit4Provider.java:238)
        at org.apache.maven.surefire.junit4.JUnit4Provider.invoke(JUnit4Provider.java:159)
        at org.apache.maven.surefire.booter.ForkedBooter.invokeProviderInSameClassLoader(ForkedBooter.java:383)
        at org.apache.maven.surefire.booter.ForkedBooter.runSuitesInProcess(ForkedBooter.java:344)
        at org.apache.maven.surefire.booter.ForkedBooter.execute(ForkedBooter.java:125)
        at org.apache.maven.surefire.booter.ForkedBooter.main(ForkedBooter.java:417)
Caused by: io.cucumber.datatable.CucumberDataTableException: 'jcucumberng.project.domain.User' could not transform
      | group    | gpao     |
      | username | svgpao1  |
      | password | REDACTED |

Answer:

You need to also modify the transform method in addition to @Transpose.

public User transform(DataTable dataTable) throws Throwable {
    return new User(dataTable.asMaps().get(0));
}

@When("I Log In As Valid User")
public void I_Log_In_As_Valid_User(@Transpose SilverUser user) throws Throwable {
    System.out.println(user);
}

Question:

Original Title: What does scalar mean in Cucumber DataTables in Java?

From this reference:

Java provides several scalar types. These include primitive numeric types, plus boolean and char.

Every scalar (primitive) type has an associated wrapper class or reference type.

Reading the javadocs:

/**
  * Converts the table to a List.
  *
  * If {@code itemType} is a scalar type the table is flattened.
  *
  * Otherwise, the top row is used to name the fields/properties and the remaining
  * rows are turned into list items.
  *
  * @param itemType the type of the list items
  * @param <T>      the type of the list items
  * @return a List of objects
  */
public <T> List<T> asList(Class<T> itemType) {
    return tableConverter.toList(this, itemType);
}

/**
  * Converts the table to a List of List of scalar.
  *
  * @param itemType the type of the list items
  * @param <T>      the type of the list items
  * @return a List of List of objects
  */
public <T> List<List<T>>> asLists(Class<T> itemType) {
    return tableConverter.toLists(this, itemType);
}

However, I was able to pass String.class in asList():

List<String> list = dataTable.asList(String.class);

A String is not a primitive in Java. I would like some clarification on what "scalar" means in this context.


Answer:

Quoting the generated code snippet:

// 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)

"Scalar" from the Cucumber javadocs may have been incorrectly used to collectively mean the associated wrapper class of the primitives (i.e. scalar) among other things.


From below example, asList() proceeds to create a List of the user defined Expense object as per docs:

Otherwise, the top row is used to name the fields/properties and the remaining rows are turned into list items.

I have observed the following:

  1. The top row (header) in the Feature file must match the field names of the object.
  2. The constructor may accept all fields as parameters.

User Defined Object (Non-Scalar):

public class Expense {

    private String name = null;
    private String amount = null;
    private String frequency = null;

    public Expense(String name, String amount, String frequency) {
        this.name = name;
        this.amount = amount;
        this.frequency = frequency;
    }

    // Getters and setters
}

Feature:

When I Enter My Regular Expenses
  | name        | amount | frequency     |
  | Electricity |   5500 | Monthly       |
  | Water       |    900 | Weekly        |
  | Internet    |   1900 | Every 2 Weeks |
  | Cable TV    |    555 | Daily         |

Step Def:

@When("^I Enter My Regular Expenses$")
public void I_Enter_My_Regular_Expenses(DataTable dataTable) throws Throwable {
  List<Expense> expenseList = dataTable.asList(Expense.class);

  for (Expense expense : expenseList) {
    System.out.println(expense);
  }

  // Here, asList() creates a List of Expense objects.
}

Output:

Question:

I am trying to follow an example in the Cucumber tutorial but it is written for Ruby and I am trying to write it in Java. I am having difficulty implementing the @When step as it requires me to update the DataTable and I am getting the following exception thrown,

java.lang.UnsupportedOperationException
at java.base/java.util.Collections$UnmodifiableList.set(Collections.java:1308)
at cucumber_tutorial.expressive_scenarios.chapter5.features.step_definitions.BoardSteps.player_x_plays_in_row_column(BoardSteps.java:36)
at ✽.When player x plays in row 2, column 1(tic_tac_toe.feature:8)

My Feature is as follows,

Feature:
  Scenario:
    Given a board like this:
      |   | 1 | 2 | 3 |
      | 1 |   |   |   |
      | 2 |   |   |   |
      | 3 |   |   |   |
    When player x plays in row 2, column 1
    Then the board should look like this:
      |   | 1 | 2 | 3 |
      | 1 |   |   |   |
      | 2 | x |   |   |
      | 3 |   |   |   |

After running the Feature and taking the generated Code snippets I have the following (I've added a couple of lines myself as well),

package cucumber_tutorial.expressive_scenarios.chapter5.features.step_definitions;

import cucumber.api.DataTable;
import cucumber.api.java.en.Given;
import cucumber.api.java.en.Then;
import cucumber.api.java.en.When;
import java.util.List;

public class BoardSteps {

    List<List<String>> boardList;

    @Given("^a board like this:$")
    public void a_board_like_this(DataTable dataTable) throws Throwable {
        boardList = dataTable.raw();
        System.out.println(boardList);
        // 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();
    }

    @When("^player x plays in row (\\d+), column (\\d+)$")
    public void player_x_plays_in_row_column(int row, int col) throws Throwable {
        // Write code here that turns the phrase above into concrete actions
        //board.
        //throw new PendingException()
        boardList.get(row).set(col, "x");
    }

    @Then("^the board should look like this:$")
    public void the_board_should_look_like_this(DataTable expectedTable) 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)
        expectedTable.diff(boardList);
        //throw new PendingException();
    }

}

The problem seems to be that dataTable.raw() in the @Given step assigns an Unmodifiable Collection type to boardList and so makes it un-updateable. The Ruby example simply has, ​ 

row, col = row.to_i, col.to_i​   
@board[row][col] = ​'x'​

my exception is thrown from,

boardList.get(row).set(col, "x"); //this line throws the exception

Can someone please advise me what the standard way is to update a DataTable using Java ?


Answer:

As @Grasshopper suggested in the above comments, I have implemented a conversion function convertDataTableToModifiableList to convert the unmodifiable DataTable into a List<List<String>> object which I can update. My working solution is now as shown,

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

import java.util.ArrayList;
import java.util.List;

public class BoardSteps {

    List<List<String>> boardList;

    @Given("^a board like this:$")
    public void a_board_like_this(DataTable dataTable) throws Throwable {
        boardList = convertDataTableToModifiableList(dataTable);
        System.out.println(boardList);
        // 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();
    }

    @When("^player x plays in row (\\d+), column (\\d+)$")
    public void player_x_plays_in_row_column(int row, int col) throws Throwable {
        // Write code here that turns the phrase above into concrete actions
        //board.
        //throw new PendingException()
        boardList.get(row).set(col, "x");
    }

    @Then("^the board should look like this:$")
    public void the_board_should_look_like_this(DataTable expectedTable) 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)
        expectedTable.diff(boardList);
        //throw new PendingException();
    }

    private List<List<String>> convertDataTableToModifiableList(DataTable dataTable){
        List<List<String>> lists = dataTable.asLists(String.class);
        List<List<String>> updateableLists = new ArrayList<>();
        for (int i = 0; i < lists.size(); i++){
            List<String> list = lists.get(i);
            List<String> updateableList = new ArrayList<>();
            for (int j = 0; j < list.size(); j++){
                updateableList.add(j, list.get(j));
            }
            updateableLists.add(i, updateableList);
        }
        return updateableLists;
    }

}

I am surprised there isn't a more elegant way of doing this, if you have a better suggestion please let me know.

Question:

I am using a cucumber datatable:

When I populate a field with a new value
      | FieldName  | FieldValue |
      | Name | Joe Blogs |
      | Email address | jblogs@gmail.com |
      | Phone 1 | 04 555 6666 |
      | Phone 2 | 0800 123 4567 |
      | SMS Phone | 023 222 333 |
      | Fax number | 09 888 9999 |
      | Location | Bermuda Triangle |

With the following java class:

@When("^I populate a field with a new value$")
    public void ShouldPopulateFieldsWithValues(DataTable arg1) throws Throwable {
        List<Map<String,String>> data=arg1.asMaps(String.class,String.class);
        //Declare a string variable for NAME and assign it's value
        String profileNameTextboxValue = data.get(0).get("FieldValue");
        //Find the profile NAME text box
        WebElement profileNameTextbox = driver.findElement(By.id("name"));
        //Clear the value from the profile NAME text box
        profileNameTextbox.clear();
        //Send the string value to the profile NAME text box
        profileNameTextbox.sendKeys(profileNameTextboxValue);

        //Declare a string variable for EMAIL and assign it's value
        String profileEmailValue = data.get(1).get("FieldValue");
        //Find the profile EMAIL text box
        WebElement profileEmailTextbox = driver.findElement(By.id("email"));
        //Clear the value from the profile EMAIL text box
        profileEmailTextbox.clear();
        //Send the string value to the profile EMAIL text box
        profileEmailTextbox.sendKeys(profileEmailValue);

        //Declare a string variable for PHONE1 and assign it's value
        String profilePhone1Value = data.get(2).get("FieldValue");
        //Find the profile PHONE1 text box
        WebElement profilePhone1Textbox = driver.findElement(By.id("phone"));
        //Clear the value from the profile PHONE1 text box
        profilePhone1Textbox.clear();
        //Send the string value to the profile PHONE1 text box
        profilePhone1Textbox.sendKeys(profilePhone1Value);

        //Declare a string variable for PHONE2 and assign it's value
        String profilePhone2Value = data.get(3).get("FieldValue");
        //Find the profile PHONE2 text box
        WebElement profilePhone2Textbox = driver.findElement(By.id("phone2"));
        //Clear the value from the profile PHONE2 text box
        profilePhone2Textbox.clear();
        //Send the string value to the profile PHONE2 text box
        profilePhone2Textbox.sendKeys(profilePhone2Value);

        //Declare a string variable for SMS Phone and assign it's value
        String profileSMSValue = data.get(4).get("FieldValue");
        //Find the profile sms phone text box
        WebElement profileSMSTextbox = driver.findElement(By.id("sms_phone"));
        //Clear the value from the profile sms phone text box
        profileSMSTextbox.clear();
        //Send the string value to the profile sms phone text box
        profileSMSTextbox.sendKeys(profileSMSValue);

        //Declare a string variable for Fax Number and assign it's value
        String profileFaxValue = data.get(5).get("FieldValue");
        //Find the profile fax text box
        WebElement profileFaxTextbox = driver.findElement(By.id("fax"));
        //Clear the value from the profile fax text box
        profileFaxTextbox.clear();
        //Send the string value to the profile fax text box
        profileFaxTextbox.sendKeys(profileFaxValue);

        //Declare a string variable for Location and assign it's value
        String profileLocationValue = data.get(6).get("FieldValue");
        //Find the profile Location text box
        WebElement profileLocationTextbox = driver.findElement(By.id("location"));
        //Clear the value from the profile Location text box
        profileLocationTextbox.clear();
        //Send the string value to the profile Location text box
        profileLocationTextbox.sendKeys(profileLocationValue);
}

The above code works, but surely there is a more efficient way to write the java class?

I was hoping that I could find a way to iterate through the WebElement variables with the different values from the datatable. Is there anyway to do this?

I found two examples of code, which I will try and adapt but not quite sure how to put it all together..

1st example:

for (Map.Entry<String, String> entry : countries.entrySet()) {
        String key = entry.getKey();
        String value = entry.getValue();
        // ...
        }

2nd example :

List<Map<String, Object>> list; // this is what you have already

    for (Map<String, Object> map : list) {
    for (Map.Entry<String, Object> entry : map.entrySet()) {
    String key = entry.getKey();
    Object value = entry.getValue();
    }
    }

Note: I am new to java / coding, any critique on the code would be helpful..

@homaxto, Taking instruction, I have created the following bean class Person..

public class Person implements java.io.Serializable {

    // Properties
        private String Username;
        private String Name;
        private String Email;
        private String Phone1;
        private String Phone2;
        private String SMS;
        private String Fax;
        private String Location;
        private String Address;
        private String Zip;
        private String PayPal;

    // Getters
        public String getUsername() { return Username; }
        public String getName() { return Name; }
        public String getEmail() { return Email; }
        public String getPhone1() { return Phone1; }
        public String getPhone2() { return Phone2; }
        public String  getSMS() { return SMS; }
        public String getFax() { return Fax; }
        public String getLocation() {return Location; }
        public String getAddress() { return Address; }
        public String getZip() { return Zip; }
        public String getPaypalEmail() { return PayPal; }

    // Setters
    public void setUsername() { this.Username = Username; }
    public void setName() { this.Name = Name; }
    public void setEmail() { this.Email = Email; }
    public void setPhone1() { this.Phone1 = Phone1; }
    public void setPhone2() { this.Phone2 = Phone2; }
    public void setSMS() { this.SMS = SMS; }
    public void setFax() { this.Fax = Fax; }
    public void setLocation() {this.Location = Location; }
    public void setAddress() { this.Address = Address; }
    public void setZip() { this.Zip = Zip; }
    public void setPaypalEmail() { this.PayPal = PayPal; }
}

My datatable is as follows:

Feature: Update my personal details
  As a Property Manager
  I want to update my personal details
  So that I can be reached by my customers

  @wip
  Scenario: Update my personal details
    Given I am logged in to my account
    Given I have navigated to the change profile tab
    When I populate a field with a new value
    | Username | Name        | Email         | Phone1     | Phone2       | SMS   | Fax  | Location         | Address        | Zip  | PayPal |
    | testy    | Test Logger | testerslog1@gmail.com | 04 555 6666 | 0800 123 4567 | 029 295 495 | 04 888 9999 | Bermuda Triangle | 5 Pokemon Lane | 9999 | testersLog1@gmail.com |
    Then I click save

Answer:

I would do something like the following. For this to work you will have to use the ID of the input elements as the value in the first column. If you have to have less dependencies in your datatable you could map this in your step implementation and look it up before using it. Also you do not need a header row, and keeping you just need to skip the first row.

@When("^I populate a field with a new value$")
    public void ShouldPopulateFieldsWithValues(DataTable dataTable) throws Throwable {
        List<List<String>> rows = dataTable.asLists(String.class);
        // Either quit having a header in your datatable or remove the first row
        rows.remove(0);
        for (List<String> row : rows) {
            String fieldName = row.get(0);
            String fieldValue = row.get(1);

            // Use the IDs as name in your datatable
            WebElement profileNameTextbox = webdriver.findElement(By.id(fieldName));
            profileNameTextbox.clear();
            profileNameTextbox.sendKeys(fieldValue);
        }
    }

Another way going around this problem could be by turning around the table having the fieldnames as column names.

When I populate a field with a new value
  | Name      | Email address    | Phone 1     | Phone 2       | SMS Phone   | Fax number  | Location         |
  | Joe Blogs | jblogs@gmail.com | 04 555 6666 | 0800 123 4567 | 023 222 333 | 09 888 9999 | Bermuda Triangle |

With this design you can create a bean class "Person" with getters and setters for each column. Cucumber will do the camel-casing and automatically call a method called setEmailAddress() etc. So if the header column is called "Email address" then Cucumber will call a method called setEmailAddress with the value jblogs@gmail.com. This will give you the following implementation signature

@When("^I populate a field with a new value$")
    public void iPopulateAFieldWithANewValue(List<Person> persons) throws Throwable {
    for (Person p : persons) {
        ...
    }
}

As you might see this easily allows many rows of data. Which one is best in your situation is entirely up till you, I just wanted to show the other option you have which is not as well documented.

Question:

I have below feature file with Given annotation

Given user have below credentials
|user       |password |
|cucumber1  |cucumber |
|cucumber2  |cucumber |

And i'm created below datamodel

public Class DataModel{
   public string user;
   public String password;
}

Trying to fetch data into the cucumber stepdefinition as below

Public Class stepdefinition {
 @Given("^user have below credentials$")
   Public void user_have_below_credintials(List<DataModel> dm){

       //Iterator or foreach is required to fetch row,column data from dm
   }
}

Please help me how can I Iterate object 'dm' to get row and column values


Answer:

// The old way
for (int i = 0; i < dm.size(); i++) {
    DataModel aDataModel = dm.get(i);
    String username = aDataModel.user;
    String password = aDataModel.password;
}

// A better way if java5+
for (DataModel aDataModel : dm) {
    String username = aDataModel.user;
    String password = aDataModel.password;
}

// another way if java8+
dm.forEach(aDataModel -> {
    String username = aDataModel.user;
    String password = aDataModel.password;
});

Note that the variables won't be available outside the loop with the way I wrote it. Just a demonstration of iterating and accessing the properties of each DataModel in your list.

A thing to keep in mind is that you're describing your list of DataModel objects as a data table. But it's not a table, it's simply a collection of values contained in an object, which you have a list of. You may be displaying it, or choosing to conceptualize it as a data table in your head, but the model that your code is describing isn't that, which means you aren't going to iterate through it quite like a table. Once you access a "row", the "columns" have no defined order, you may access them in any order you want to the same effect.

Question:

I have the following scenario in Cucumber-jvm and wondering whats the best way of writing this.

Given I create a process
When I execute the following tasks with parameters:
|Task Name| param1 | param2| param3|
...
Then each task should have outcomes:
|Task Name| outcome1 | outcome2| outcome3|

Whats the best way of approaching this?

I need to execute the When Task1, Then Task1 followed by When Task2 Then Task2 etc due to state information being lost when the next task is started. Instead of invoking all of the When Task1,2,3 first followed by the Then Task1,2,3 first.

There will also be a lot of tasks +50 so splitting this into separate steps is not ideal.

I could merge the When/Then into one step but that doesn't seem right.

Any suggestions?


Answer:

How about using a Scenario Outline. This will run each task as a scenario in itself.

To avoid the Given step being repeated you can setup a static boolean variable in your step definition java and check it as a flag.

Scenario Outline:
Given I create a process
When I execute the following task <TaskName> with parameters:
|<Parameter1>|<Parameter2>|<Parameter3>|
Then each task <TaskName> should have outcomes:
|<Outcome1>|<Outcome2>|<Outcome3>|

Examples:
|TaskName|Parameter1|Parameter2|Parameter3|Outcome1|Outcome2|Outcome3|
|task1|t1param1|t1param2|t1param3|t1out1|t1out2|t1out3|
|task2|t2param1|t2param2|t2param3|t2out1|t2out2|t2out3|
.......

In case you have variable number of parameters and outcomes, modify them by using a symbol delimited string. You can use the @Transform annotation in the step definition to get an object of parameters or outcomes.

Scenario Outline:

Given I create a process
When I execute the following task <TaskName> with parameters <parameters>
Then each task <TaskName> should have outcomes <outcomes>

Examples:
| TaskName | Parameters | Outcomes |
| task1 | t1param1,t1param2,t1param3| t1out1,t1out2,t1out3 |
| task2 | t2param1,t2param2,t2param3| t2out1,t2out2,t2out3 |
.......

If there is any dependency of of one task on the outcome of the other task then you have to be careful in how you handle them. You could even add a reset step like, killing current process etc, after your current Then step if required in any of the tasks.


Last scenario ---This is a big hack dependent on the scenario id remaining same. Add the count of scenarios in the example table for the last step as below.

Scenario Outline:

    Given I create a process
    When I execute the following task <TaskName> with parameters <parameters>
    Then each task <TaskName> should have outcomes <outcomes>
    ***And Last step to run for last scenario 3***

    Examples:
    | TaskName | Parameters | Outcomes |
    | task1 | t1param1,t1param2,t1param3| t1out1,t1out2,t1out3 |
    | task2 | t2param1,t2param2,t2param3| t2out1,t2out2,t2out3 |
    | task3 | t3param1,t3param2,t3param3| t3out1,t3out2,t3out3 |


Include in StepDefinition.java

private Scenario scenario;

@Before
public void before(Scenario sce) {
    this.scenario = sce;
    System.out.println("SCENARIO ID -- " +scenario.getId());
}

You will get a string like for scenario outline - **feature-description ; scenariooutline-description ; example-description ; rownumber + 1**. For example -- validating-sample;so1;se1;2. This will be for the first row of the examples table.

For scenario outline case you can split with delimiter ";" and use the last part after subtracting 1. Put this logic in a method getCurrentExamplesRow()

@Then("^Last step to run for last scenario (\\d+)$")
public void lastStep(int size) {

    // Will be called only for last scenario in examples... 
    if(size==getCurrentExamplesRow()) {

    }
}

Question:

I'm using cucumber for BDD and in one feature have this type of datatable, how can I differentiate in cucumber generated method which class should be used

And the following set of "Toys"
    | name           |something1| 
    | plane          |  400     |  
    | ball           |  800     |  
 And the following set of "Shoes"
    | name           | something2| 
    | boots          |   35      | 
    | sandals        |   35      | 

This implementation throws exception Can't convert a table to java.util.List. When using List, SomeComplexType must not be a generic type

@Given("^the following set of \"([^\"]*)\"$")
public <T> void the_following_set_of(String type, List<T> info) throws Throwable {
    switch (type) {
    case "Toys":            

        break;
    case "Shoes":
        break;
    default:
        break;
    }
}

any other suggestions?


Answer:

You won't be able to get automatic conversion to a list of typed objects if the type is also a parameter because Cucumber will not know which type it should be converting to when it calls the definition. You would have to use the code you have above and do your conversion manually. Cucumber will do the conversion for you if you have a method per type though, e.g.:

@When("^the following set of toys")
public void the_following_set_of(List<Toy> toys) throws Throwable {
    System.out.println(toys);
}
// ... another method for shoes, etc.

Then, just define a simple bean for toy with the same property names you are using in your table headers so Cucumber knows which fields to populate:

public class Toy {
    private String name;
    private String something1;

    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
    // ... etc.
}

Cucumber should now call your method with a populated list of Toy's for the clause "And the following set of toys".

Question:

How would I create a data table in the background and access its data in the scenario?

I am running scenarios with variations of values ​​and data and would like to know if I can access the values ​​entered in a backgorund table

Background: 
Given Essas informacoes:
  | Price | Name | Insurance     | Damage           | Type     | Email |
  |       |      |  3.000.000,00 | Full Coverage    | Gold     |       |
  |       |      | 10.000.000,00 | No Coverage      | Silver   |       |
  |       |      | 25.000.000,00 | Partial Coverage | Platinum |       |
  |       |      |               |                  | Ultimate |       |
  |       |      |               |                  |          |       |
And E o navegador aberto
And O site carregado
And Tendo selecionado Truck
@ValidarCotas  
Scenario: Selecionar cotacao de seguro de tipo diferente de acordo com valores variados
        Given Esta na tela do formulario de Truck
        And Preenchido os dados do veiculo e os dados do segurado
        When Preencho os campos do formulario de dados do seguro  "**<Insurance>**" "**<Damage>**"
        And Seleciono cota de seguro "<Type>"
        Then Valido se a cota foi enviada para o email
        And Valido se o valor da cota for o mesmo que eu selecionei no site
        When Fecho o navegador

Answer:

As per my understanding you want to reuse your examples across multiple scenarios. AFAIK it is not supported when using gherkin with cucumber.

One of the way is to reuse examples between scenarios by moving examples in external file and referencing it in your scenario which is supported using gherkin with qaf or BDD2 with qaf but not cucumber.

For example:

Background: 
Given E o navegador aberto
And O site carregado
And Tendo selecionado Truck

@ValidarCotas
@dataFile:resources/data/testdata.csv   
Scenario: Selecionar cotacao de seguro de tipo diferente de acordo com valores variados
        Given Esta na tela do formulario de Truck
        And Preenchido os dados do veiculo e os dados do segurado
        When Preencho os campos do formulario de dados do seguro  "**<Insurance>**" "**<Damage>**"
        And Seleciono cota de seguro "<Type>"
        Then Valido se a cota foi enviada para o email
        And Valido se o valor da cota for o mesmo que eu selecionei no site
        When Fecho o navegador

Refer documentation for more details.

Question:

Hi I'm testing using cucumber currently and I'm a bit stumped the error message isn't helping me too much even after trawling the net I still can't find the correct solution.

   When company send us these XML messages   
     |fixtures/entitlements/sport_tennis.xml|
     |fixtures/entitlements/sport_golf.xml|

    @When("^company send us these XML messages$")
public void company_send_us_these_XML_messages(DataTable messages) throws Throwable {
   client.publish(messages);


   try {
       Thread.sleep(500);
   } catch (InterruptedException e) {
       e.printStackTrace();
   }
   }

I'm currently getting the error message in the subject title.

Error java: incompatible types: cucumber.api.DataTable cannot be converted to java.lang.String

How do I handle this datatable. The message doesn't really help I turn it into a string and it then tells me I need to Map it or make it into a data table. Are there any workarounds/solutions. This one has me stumped.


Answer:

Modify the step definition to this

public void company_send_us_these_XML_messages(List<List<String>> messages) {


}

You can loop through this to get your messages as a collection or a string to send to your method.