How do I create a Cucumber DataTable?
Asked Answered
P

4

10

I want to manually set up a Cucumber DataTable using Java (instead of Gherkin).

In Gherkin, my table would look like this:

| h1 | h2 |
| v1 | v2 |

My Java so far looks like this:

List<String> raw = Arrays.asList( "v1", "v2");
DataTable dataTable = DataTable.create(raw, Locale.getDefault(), "h1", "h2");

What I get back is a DataTable with headers but no contents. It's also longer than expected:

  | h1| h2 |
  |   |    |
  |   |    |

I'm sure the solution must be fairly simple, but I'm a bit at a loss right now. What do I need to do to get my table?

Primrosa answered 11/2, 2014 at 14:24 Comment(1)
I think I got it: List<List<String>> raw = Arrays.asList( Arrays.asList("h1", "h2"), Arrays.asList("v1", "v2") ); DataTable dataTable = DataTable.create(raw, Locale.getDefault(), "h1", "h2"); But the last two parameters for the create method don't seem to get used - and I'm sure there must be a more elegant way than what I did there...Primrosa
D
11

Hope this helps. If you're full Gherkins step looks like this...

When I see the following cooked I should say:
  | food  | say     |
  | Bacon | Yum!    |
  | Peas  | Really? |

You want this in Java. (Note that the cucumber.api.DataTable passed in is setup with your expected values before the test).

@When("^I see the following cooked I should say:$")
public void theFoodResponse(DataTable expectedCucumberTable) {
    // Normally you'd put this in a database or JSON
    List<Cuke> actualCukes = new ArrayList();
    actualCukes.add(new Cuke("Bacon", "Yum!"));
    actualCukes.add(new Cuke("Peas", "Really?")); 

    Another link to a Full Example.diff(actualCukes)
}

I will say that in the examples by Aslak Hellesøy he doesn't actually use the DataTable.

He would do your example something like this:

@When("^I see the following cooked I should say:$")
public void theFoodResponse(List<Entry> entries) {
    for (Entry entry : entries) {
        // Test actual app you've written
        hungryHuman.setInputValue(entry.food);
        hungryHuman.setOutputValue(entry.say);
    }
}

public class Entry {
    String food;
    String say;
}

For a Full Example for more reading check out:

  • Gherkins calculator: link
  • Java calculator Step Definitions: link

EDIT:

Sorry for the overkill @Christian, you perhaps didn't need the whole context of how to use it in an app, just a clean way to use DataTable.create and most of what I posted was another way to skin that cat with the Entry class (Which might be helpful for those reading this later.)

So the way you did it in your comment wasn't far off. I'm no pro at collections, so I can't give you any tips on making your 2D List of Strings, but I can clarify on the last two parameters (if you're using all 4).

  • If your columns are using Date or Calendar fields you can indicate a Format in the second to last param.
  • If you don't use the last one for Column Names, then it will automatically read your top line string for the column names.
  • Also you can drop the Locale.getDefault() and it will do that for you; so then you're looking at:

.

List<List<String>> infoInTheRaw = Arrays.asList( Arrays.asList("h1", "h2"),
    Arrays.asList("v1", "v2") ); 
DataTable dataTable = DataTable.create(infoInTheRaw);

You could also use the constructor which would be equally messy. :)

Dugald answered 12/2, 2014 at 20:22 Comment(5)
Thanks Eric. The thing I don't quite understand is why it only uses the columnHeaders params in a very limited amount of cases: DataTable.create() passes things on to tableConverter.toTable(), where it becomes apparent that they don't necessarily get picked up. I wish the docs were any better; we're left to guessing or looking through the entire source code, it seems.Primrosa
P.S.: The other problem really is that - if you want to create a DataTable yourself, not using a feature file - you can end up with just the header row and no values, as I did...Primrosa
I agree with you. We shouldn't have to read through all the source code to figure this out. I ran into the same issue when trying to tie in Webdriver, where the docs didn't explain even to allow you to use what was there without hunting through the source. Aslak is a super smart dude, and very on top of things. When I've tried to update the docs for him on Git he spent more time telling me why they didn't need to be updated though, then on just pushing them out and tailoring the lingo to meet what he was looking for.Dugald
Cucumber is a powerful tool, but for jvm I have been very flustered when trying to implement it in real world tests, so I'm waiting to see if it matures for a spell. Hope you can finagle your code so you can get DataTable.create() to eat more than feature files without too much hacking. :)Dugald
@Primrosa if you'd like to help improving the cucumber docs, please have a look here: github.com/cucumber/docs.cucumber.io - we're currently working on it, and could always use some help :)Burette
D
4

Instead of DataTable we can actually create List of custom object using the same fields as in data table.

for e.g.

Then the book list response should contain records as
      | book_id | book_title  | book_cost | 
      | B0001   | Head First Java  | 350|
      | B002    | Head First Spring| 550|

Now your Step class should define a Step as

 @Then("^the book list response should contain records as$")
   public void validateBookRecords(List<Book> booksList) throws Throwable { //assertions will go here}

And your Book class should look like

    public class Book {
      public String book_id;
      public String book_title;
      public String book_cost;
   }

Since the fields are all public so no getter/setter are needed. If you decide to declare them private (dont see a reason why) then ensure to add getters/setters as well.

Hope this helps

Donald answered 16/2, 2016 at 12:39 Comment(1)
Click here for related reading.Posse
S
0

the cucumber dataTable can be build from nested lists where the first List in the nested lists contains the names of the columns and the others are the data lists per rows:

public static DataTable createTestDataTable() {
    List<List<String>> dtList = new ArrayList<>();
    List<String> columns = Arrays.asList("col1", "col2", "col3");
    List<String> dataRow1 = Arrays.asList("r1c1", "r1c2", "r1c3");
    List<String> dataRow2 = Arrays.asList("r2c1", "r2c2", "r2c3");
    dtList.add(columns);
    dtList.add(dataRow1);
    dtList.add(dataRow2);
    return DataTable.create(dtList);
}

This corresponds to:

|col1|col2|col3|  <-- columns
|r1c1|r1c2|r1c3|  <-- data row 1
|r2c1|r2c2|r2c3|  <-- data row 2
Syllogize answered 2/9, 2021 at 15:54 Comment(0)
L
0

this is the method that I use to extract the first row as a table headers from the Cucumber dataTabe object.

for this scenario:

When I do some stuff
  | header1 | header2 | header3 | header4 |
  | N       | do      |         |         |
  | Nu      |         | some    | stuff   |
  | qwerty  |         | some    | stuff   |
  | cook    | do      |         |         |

this solution:

    public void i_consume_datatable(DataTable table) {
        //this is the little optimized version 
        List<List<String>> dataLists = table.asLists(String.class);
        if (dataLists.size() > 1) {
            String[] headers = dataLists.get(0).toArray(new String[0]);
            dataLists.stream().skip(1).map(rowList -> {
                Map<String, String> row = new LinkedHashMap<>();
                IntStream.range(0, headers.length).forEach(i -> row.put(headers[i], rowList.get(i)));
                return row;
            }).forEach(map -> yourActualMethodWhichDoTheWork(map.get(“header1”), map.get("header2”),    map.get("header3”), map.get("header4”)));
        }
    }
Lee answered 15/2, 2023 at 17:17 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.