Cucumber-JVM 3 - io.cucumber.datatable.UndefinedDataTableTypeException
Asked Answered
P

5

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

Phallus answered 9/6, 2018 at 7:16 Comment(5)
Issue #1388 is now open in GitHub.Phallus
I am attempting a kind of guide for Cucumber 3 migration at this link - github.com/grasshopper7/cuke3-migration. Just started and is a total work in progress. Maybe this helps out.Plenish
@Plenish Lovely writeup. You may want to contact the guys at cucumber.io. Their docs are outdated in a lot of places.Phallus
Finally got time to finish up. Divided into 2 - parameter github.com/grasshopper7/cuke3-migrate-parametertype and datatable github.com/grasshopper7/cuke3-migrate-datatabletype. Would appreciate any feedback. ThanksPlenish
@Plenish Raised Issue #1 in your repo. Thanks!Phallus
P
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;
Plenish answered 9/6, 2018 at 8:5 Comment(5)
Hi, can you also include the import statements? I am getting multiple suggestions.Phallus
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).Phallus
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.3Plenish
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.Phallus
This is such a big loss. Previously I could use List<Income> directly. Very handy for entities with 10-20 fields. Now I have to "map" them manually. Makes no sense at all.Multifold
A
5

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    
}
Almagest answered 26/10, 2020 at 14:40 Comment(0)
P
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.

Phallus answered 9/6, 2018 at 10:18 Comment(0)
R
0

If you wan to have an automatic mapping between your dataTable and your bean, you can use Cucumber datatable to bean mapping library. Add just an annotation @DataTableWithHeader on your bean, and the mapping will be automatically done. We use this library instead of custom mapping:

  • header are validated, if you set a non existing header on your table, an explicit error will be throw
  • you can map column values on all existing java types
  • there is less mapping code to maintain
Rebus answered 22/7, 2024 at 6:59 Comment(0)
G
-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    
}
Grammarian answered 18/3, 2020 at 12:58 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.