8

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: enter image description here

k_rollo
  • 5,304
  • 16
  • 63
  • 95
  • 1
    Issue [#1388](https://github.com/cucumber/cucumber-jvm/issues/1388) is now open in GitHub. – k_rollo Jun 10 '18 at 03:24
  • 1
    I am attempting a kind of guide for Cucumber 3 migration at this link - https://github.com/grasshopper7/cuke3-migration. Just started and is a total work in progress. Maybe this helps out. – Grasshopper Jun 11 '18 at 13:02
  • @Grasshopper Lovely writeup. You may want to contact the guys at cucumber.io. Their docs are outdated in a lot of places. – k_rollo Jun 12 '18 at 02:46
  • 1
    Finally got time to finish up. Divided into 2 - parameter https://github.com/grasshopper7/cuke3-migrate-parametertype and datatable https://github.com/grasshopper7/cuke3-migrate-datatabletype. Would appreciate any feedback. Thanks – Grasshopper Jul 12 '18 at 16:29
  • @Grasshopper Raised [Issue #1](https://github.com/grasshopper7/cuke3-migrate-parametertype/issues/1) in your repo. Thanks! – k_rollo Aug 14 '18 at 05:21

4 Answers4

10

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;
Grasshopper
  • 8,908
  • 2
  • 17
  • 32
  • Hi, can you also include the import statements? I am getting multiple suggestions. – k_rollo Jun 09 '18 at 08:50
  • Thanks, I added the imports. I copied the `Configurer` class in the same package where the *Steps.java classes are but I am getting above error for constructor (see added screenshot). – k_rollo Jun 09 '18 at 09:15
  • Can u add the whole class including the imports section? What is the version of datatable.jar and datatable-dependencies in imported maven jars? I have 1.0.3 – Grasshopper Jun 09 '18 at 09:25
  • I got it to work. I needed to import the (custom) Income.class. The red underlines were overlapping in Eclipse IDE. Thank you, Grasshopper, your links and example were absolutely helpful. Upvoted and accepted answer. My datatable jars are also 1.0.3. – k_rollo Jun 09 '18 at 09:59
  • This is such a big loss. Previously I could use `List` directly. Very handy for entities with 10-20 fields. Now I have to "map" them manually. Makes no sense at all. – ACV Sep 01 '22 at 18:36
3

Use DataTableType annotation

@DataTableType
public Income incomeEntry(Map<String, String> entry) {
    return new Income(entry.get("name"), entry.get("amount"), entry.get("frequency"));
}

Then you can directly use the list of the custom class in step definition

@When("^I Enter My Regular Income Sources$")
public void I_Enter_My_Regular_Income_Sources(List<Income> incomes) throws Throwable {
    // More code    
}
2

Migrating from v2.x.x to v3.x.x for DataTable

Posting my answer to serve as reference for those who may encounter the same. For their release announcement, click here.

I decided to put DataTableConfigurer.java in its own package so it does not mix with my stepdefs: enter image description here

Runner:

@CucumberOptions(features = { "src/test/resources/features" }, tags = { "not @ignore" }, glue = {
        "jcucumberng/steps/defs", "jcucumberng/steps/config", "jcucumberng/steps/hooks" }, ...

DataTableConfigurer:

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

import cucumber.api.TypeRegistry;
import cucumber.api.TypeRegistryConfigurer;
import io.cucumber.datatable.DataTableType;
import io.cucumber.datatable.TableEntryTransformer;
import jcucumberng.steps.domain.Expense;
import jcucumberng.steps.domain.Income;

/*
 * Maps datatables in feature files to custom domain objects.
 */
public class DataTableConfigurer implements TypeRegistryConfigurer {

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

    @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"));
            }
        }));

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

}

I had another custom domain type Expense (which happened to have the same fields), so I just registered it again based on the example.

k_rollo
  • 5,304
  • 16
  • 63
  • 95
-2
    I have created a code which won't use **DataTable** concept. You can update this below implementation so that you won't get any failures in your scripts in future.

    CucumberUtil.java:
    -----------------
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;

    public class CucumberUtil {

// public static synchronized Map<String, String> TableDictionaryConverter(DataTable table) {                    -- removed this concept because of version issues in DataTable
   public static synchronized Map<String, String> TableDictionaryConverter(List<List<String>> data) {
          Map<String, String> mapTable = new HashMap<String, String>();
          for(List<String> rows: data) {
                 mapTable.put(rows.get(0), rows.get(1)); 
          }
          return mapTable;
   }

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

// Stepdef
@When("^I Enter My Regular Income Sources$")
public void I_Enter_My_Regular_Income_Sources(List<List<String>> table) throws Throwable {

    Map<String, String> mapTable = CucumberUtil.TableDictionaryConverter(table);

    String nameValue = mapTable.get("name");         // Salary
    String amountValue = mapTable.get("name");       // 25000
    String frequencyValue = mapTable.get("name");    // every 2 weeks

    // More code    
}