6

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.

k_rollo
  • 5,304
  • 16
  • 63
  • 95

4 Answers4

9

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:

output

k_rollo
  • 5,304
  • 16
  • 63
  • 95
  • 3
    Only works until Cucumber-JVM 2.4.0. Click [here](https://stackoverflow.com/questions/50771856/cucumber-jvm-io-cucumber-datatable-undefineddatatabletypeexception) for v3.x.x. – k_rollo Jun 09 '18 at 11:41
  • Thanks Lu, this looks like something I am looking for. – msangel Nov 26 '18 at 14:41
3

For Lower Versions use the below implementation.

Datatable.feature

When Datatable to Pojo
 |  field1  |  field1Value1  | field1Value2  |
 |  field2  |  field2Value1  | field2Value2  |
 |  field3  |  field3Value1  | field3Value2  |
 |  field4  |  field4Value1  | field4Value2  |
 |  field5  |  field5Value1  | field5Value2  |

Pojo.class

import lombok.Getter;
import lombok.Setter;
import lombok.ToString;

@Getter
@Setter
@ToString
public class Pojo {

private String field1;
private String field2;
private String field3;
private String field4;
private String field5;

}

StepDefinition.class

@Given("Datatable to Pojo")
public void method (DataTable dataTable){
    List<Pojo> pojoList = new ArrayList<Pojo>();
    List<Map<String,String>> mapList = dataTable.transpose().asMaps();
    for(Map<String, String> map : mapList) {
        pojoList.add(new ObjectMapper().convertValue(map, Pojo.class));
    }
}
1

I did not find an explicit definition about what Cucumber for Java means with scalar type.

The best hint I could find was in the snippet that is produced for new steps that accept a DataTable. The generated comment reads:

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)

So it seems that besides the "Java scalar types" (byte, short, int, long, char, boolean, or respectively their wrapper types Byte, Short, Integer, Long, Char and Boolean) you can also use String, java.util.Date and enum types.

Actually, a short test showed that I can use any type that has a constructor with a single String as parameter.


A small example with my own value class (very contrived). The output from the following snippets is a List<List<MyValueClass>>.

// MyValueClass.java
public class MyValueClass {

    private final String value;

    public MyValueClass(String v) {
        this.value = v;
    }

    public String getValue() {
        return value;
    }
}

// snippet from MySteps.java
@Given("^a table with$")
public void a_table_with(DataTable arg1) throws Throwable {
    System.out.println(arg1.asLists(MyValueClass.class));
}

// snippet from my test1.feature
  Scenario: Test with Datatable
    Given a table with
      | a | b | c |
      | 1 | 2 | 3 |
      | a | b | c |
Thomas Kläger
  • 17,754
  • 3
  • 23
  • 34
  • Hi Thomas, might you provide a snippet in how you used the "any type with constructor that accepts String param" for `asList()` and `asLists()`? – k_rollo Sep 17 '17 at 23:14
0

Declare the argument as a List, but don’t define any capture groups in the expression:

If the Datatable contains only one column then Data Table is automatically flattened to a List by Cucumber (using DataTable.asList(String.class)) before invoking the step definition.

Mujeeb007
  • 41
  • 1