Java Cucumber Step Definition for vertical DataTable - cucumber

I'm using cucumber-jvm. I have the following in a .feature file:
Background:
Given the following account file line:
| First Name | Lance |
| Last Name | Fisher |
| Corporate Name | |
This is a vertically-oriented table. The following generated step definition uses First Name and Lance as the headers, where First Name, Last Name, and Corporate Name should be the headers.
#Given("^the following account file line:$")
public void the_following_account_file_line(DataTable arg1) throws Throwable {
// Express the Regexp above with the code you wish you had
// For automatic conversion, change DataTable to List<YourType>
throw new PendingException();
}
How would I implement a step definition to handle a vertical table rather than a horizontal table?

Just receive the value on parameter as Map<String, String> instead of DataTable. Cucumber will convert the first columns as map keys and the second column as map values.

It's simple enough, as the Datatable can return an array of arrays of strings, and you can step through this in whatever manner you wish.
However, I'd agree with Prakash Murthy that the horizontal table is more usual and useable.

Related

Mapping wrong number of parameter from Gherkin features file to step definition

I had feature declaration like this:
Feature: find taxi and minicabs information
in order to get taxi and minicabs contact at given location
as application developer
I want to find tax and minicabs contact information at given location or query options
Scenario Outline: find taxi and minicabs contact information
Given Joe at location with <lat> and <lon>
When get all taxi and minicabs contacts information
Then should see list of taxi and minicabs
And all of them are at location with <lat> and <lon>
Examples:
| lat | lon |
| 51.490075 | -0.133226 |
And I had step definition like this:
#Given("^Joe at location with ([+-]?([0-9]+[.])?[0-9]+) and ([+-]?([0-9]+[.])?[0-9]+)$")
public void joeAtLocationWithLatAndLon(Number lat, Number lon) throws Throwable {
....
}
I expected I can received 2 parameters but Cucumber passed to me 4 parameters.
Error message as below:
with pattern [^Joe at location with ([+-]?([0-9]+[.])?[0-9]+) and ([+-]?([0-9]+[.])?[0-9]+)$] is declared with 2 parameters. However, the gherkin step has 4 arguments [51.490075, 51., -0.133226, 0.].
Do you have any idea about this? btw, I very appreciate if you can explain the way cucumber identify the number of parameters or share me any document about that.
The issue is the two inside brackets inside the regular expression. Using the current regex you will get 2 groups - one the whole "51.490075" and second "51." which matches the exp in the ([0-9]+[.]) part. Thus 4 arguments are created.
Remove the inside brackets and you will get just one argument for each, so two in total.
The next problem you are going to have is cucumber does not know how to transform String to Number class unless you tell it. For this you need to make use of the Transform annotation and create a specific class for this.
import cucumber.api.Transformer;
public class NumberTransformer extends Transformer<Number>{
#Override
public Number transform(String value) {
return Double.parseDouble(value);
}
}
#Given("^Joe at location with ([+-]?[0-9]+[.]?[0-9]+) and ([+-]?[0-9]+[.]?[0-9]+)$")
public void joeAtLocationWithAnd(#Transform(NumberTransformer.class)Number arg1, #Transform(NumberTransformer.class)Number arg2) throws Exception {
System.out.println(arg1);
System.out.println(arg2);
}
For the transforming issue you can also look up xstreams. If you are using cucumber 2, these kind of transformation is easier by using Xstreamconvertor annotation - https://github.com/cucumber/cucumber-jvm/pull/1010

How to write scenario outline to capture list of object in a single argument?

When writing cucumber scenario outline test cases, sometimes i have requirement that i need one of the placeholder to hold a list of data instead of one. See below pseudo example:
Scenario Outline: example
Given I have <input_1>
When I choose <input_2>
Then I should receive <result_list> //instead of a single result
Examples:
| input_1 | input_2 | result |
| input_1_case_1 | input_2_case_1 | result_1_case_1_part_1 |
| | | result_1_case_1_part_2 |
| | | result_1_case_1_part_3 |
In the above example, my "result" need to capture a list of object for each single input_1 and input_2 parameter. But with the above writing, cucumber will not compile the last statement to something like:
#Then("....")
public void i_should_receive(Datatable or list of object) throws Throwable {
// Write code here that turns the phrase above into concrete actions
throw new PendingException();
}
How can write my cucumber script to achieve what i want?
Thanks.
Scenario Outline: example
Given I have <input_1>
When I choose <input_2>
Then I should receive <result_list> //instead of a single result
Examples:
| input_1 | input_2 | result
| input_1_case_1 | input_2_case_1 | result_1_case_1_part_1,result_1_case_1_part_2,result_1_case_1_part_3 |
Then in your step definition
#Then("....")
public void i_should_receive(String result) throws Throwable {
List<String> items = Arrays.asList(str.split("\\s*,\\s*"));
}
I've been able to set a list of values in a cucumber example, this way:
Scenario Outline: The role already exists in the system, with the specified permissions
Given the user admin is logged in
And the role ADMIN with permissions <permissions> to be created
When a call to SecurityService is performed to create system roles
Then http status code 200 is returned
Examples:
|permissions |
|REGISTER_ACCOUNT,REVOKE_TOKENS,GET_ROLES,SET_ROLES|
And the concrete method:
#And("^the role ([^\"]*) with permissions ([^\"]*) to be created$")
public void theRoleWithPermissionsToBeCreated(String roleCode, List<String> permissions) {
for me, this works like a charm and I receive a list of Strings directly, without the need to parse a string value

Conditional Inputs?

I'm trying to write a cucumber test for part of our application (I'm completely new to cucumber, and writing acceptance tests really), and I have a step in the test, which I want to do one of 2 different things. I want it to either check for a specific value, or check for a system default value.
This is what I came up with
Scenario Outline: English News
Given with the locale is set to '<language>'
When a user views the page
Then they see the <image> image
Examples:
| language | image |
| en-gb | default |
| fr | "french.png" |
This then hits one of 2 different step definitions,
#And("^they see the default image$")
public void defaultImage() throws Throwable {
// check for system default
}
#And("^they see the \"(.*)\" image$")
public void customImage(String image) throws Throwable {
// check for specific value from the input
}
Is this the correct way to do this? My TL has suggested that I should actually only have the one step def, and pass "default" as the input, and then do a conditional check inside the step def, like this:
#And("^they see the \"(.*)\" image$")
public void checkImage(String image) throws Throwable {
if ("default".equals(image)){
// check for system default
} else {
// check for specific value from the input
}
}
Which is the correct way to do this? Any help is greatly appreciated.
your default image must have a real name, right ? so why not validating its real name ? because "default" could be anything so not everyone may understand the same thing.
If you do this, indeed you need only one step def. I wouldn't put an if check, I would assert the real name. You can maybe add a comment in your example table saying that the first value is the default, so that it's clear for everyone in your team.

How can I get the Scenario Outline Examples' table?

In AfterScenario method, I want to get the rows from table "Examples" in Scenario Outline, to get the values in it, and search that specific values in the database
I know that this can be achieved by using Context.Scenario.Current...
Context.Scenario.Current[key]=value;
...but for some reason I'd like to be able to get it in a simpler way
like this:
ScenarioContext.Current.Examples();
----------- SCENARIO --------------------------------
Scenario Outline: Create a Matter
Given I create matter "< matterName >"
Examples:
| matterName |
| TAXABLE |
----------AFTER SCENARIO -----------------------------------
[AfterScenario()]
public void After()
{
string table = ScenarioContext.Current.Examples();
}
So if you look at the code for ScenarioContext you can see it inherits from SpecflowContext which is itself a Dictionary<string, object>. This means that you can simply use Values to get the collection of values, but I have no idea if they are Examples or not.
The best solution I came up with was to infer the examples by keeping my own static singleton object, then counting how many times the same scenario ran.
MyContext.Current.Counts[ScenarioContext.Current.ScenarioInfo.Title]++;
Of course, this doesn't work very well if you don't run all the tests at the same time or run them in random order. Having a table with the examples themselves would be more ideal, but if you combine my technique along with using ScenarioStepContext you could extract the parameters of the Examples table out of the rendered step definition text itself.
Feature
Scenario Outline: The system shall do something!
Given some input <input>
When something happens
Then something should have happened
Examples:
| input |
| 1 |
| 2 |
| 3 |
SpecFlow Hook
[BeforeStep]
public void BeforeStep()
{
var text = ScenarioStepContext.Current.StepInfo.Text;
var stepType = ScenarioStepContext.Current.StepInfo.StepDefinitionType;
if (text.StartsWith("some input ") && stepType == StepDefinitionType.Given)
{
var input = text.Split(' ').Last();
}
}

How do I create a Cucumber DataTable?

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?
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. :)
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
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
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”)));
}
}

Resources