As we are familiar with the basic gherkin syntax such as feature
, scenario
, Scenario Outline
, background
, given
, when
and then
steps already, let us discuss about the table
or tablenodes
used in the steps of a gherkin feature file.
Tables
Tables
or tablenodes
or Datatables
are used for specifying a larger data set as an argument in the steps of a scenario in a feature file. These tables are handy for passing a list of values to a step definition. The data tables are passed to the step definition of a specific step as its last argument. Let us understand with the help of an example below.
A scenario for logging in without using a data table
Scenario: login with valid credentials
Given a user has been created with email "user@email.com" and username "user" and password "password"
And the user has browsed to the login page
When the user enters email "user@email.com" and username "user" and password "password" in the login form
And the user logs in
Then the user should be redirected to the homepage
There are cases where a large amount of data needs to be set in the steps. It can be done using a number of "ands" in the step text as shown in the example above. However, good practice would be to use data tables to provide the data.
A scenario for logging in using a data table
Scenario: login with valid credentials
Given a user has been created with the following details:
| email | username | password |
| user@email.com | user | password |
And the user has browsed to the login page
When the user enters the following details in the login form:
| email | username | password |
| user@email.com | user | password |
And the user logs in
Then the user should be redirected to the homepage
Please do not confuse data tables with the example table used in a scenario outline. Even if they look alike, they have completely different purposes. Please refer to this blog to learn more about the Scenario Outline: https://dev.to/jankaritech/scenario-outline-in-gherkin-feature-file-16jh
Data tables
Unlike the examples table, a table node provides all of the data in the data table at once, in the specific step where the table is provided.
Let us look at another example of a data table. To be more clear about its significance, let us first write a scenario without using a data table as below.
Scenario: view and delete files
Given user "testuser" has been created
And user "testuser" has logged in
When the user browses to the files page
Then file "myCollegeProject" should be listed
And file "interviewQuestions" should be listed
And file "picnicCollections" should be listed
And file "presentation" should be listed
When the user deletes file "myCollegeProject"
And the user deletes file "interviewQuestions"
Then file "myCollegeProject" should not be listed
And file "interviewQuestions" should not be listed
But file "picnicCollection" should be listed
And file "presentation" should be listed
When observed closely, we can see that the listing of several files, continuous deletion of several files, non-listing of deleted files and listing of non-deleted files has been written using several repetitive steps. The steps seem to be difficult to maintain this way. Moreover, more effort is required to understand the scenario when reading it. To improve this, we can use the data tables. The example below illustrates the use of data tables in the steps where multiple data are to be passed at once. For example, multiple files are to be listed, multiple files are to be deleted, and again multiple files are expected not to be listed and to be listed respectively.
Scenario: view and delete files
Given user "testuser" has been created
And user "testuser" has logged in
When the user browses to the files page
Then the following files should be listed:
| files |
| myCollegeProject |
| interviewQuestions |
| picnicCollections |
| presentation |
When the user deletes the following files
| files |
| myCollegeProject |
| interviewQuestions |
Then the following files should not be listed:
| files |
| myCollegeProject |
| interviewQuestions |
But the following files should be listed:
| files |
| picnicCollections |
| presentation |
The data set provided using the data table should be hashed in the step definition of the particular steps in order to transform the cucumber data table into either an array of data or object.
for example:
In nightwatch, to transform a cucumber data table to an array, we use hashes
When('the user deletes the following files', (dataTable) {
const filesToDelete = dataTable.hashes()
console.log(filesToDelete);
/* It returns :
[ {files: 'myCollegeProject'},
{files: 'interviewQuestions'} ]
*/
for (const file in filesToDelete) {
this.delete(file)
}
})
Now, to transform a cucumber data table to an object, we use rowsHash
. However, rowsHash can only be called on a data table where all rows have exactly two columns
.
for example:
When the user sets the price for the following items
| book | 500 |
| sharpener | 30 |
| pencil | 15 |
The step definition for this step can parse the data table using rowsHash
as follows:
When('the user sets the price for the following items', (dataTable) {
const priceOfItems = dataTable.rowsHash()
console.log(priceOfItems)
/* It returns :
{ book : '500',
sharpener : '30',
pencil : '15' }
*/
})
Let us see what happens if we use hashes
for this example.
When('the user sets the price for the following items', (dataTable) {
const priceOfItems = dataTable.hashes()
console.log(priceOfItems)
/* It returns :
[ { book : 'sharpener', '500' : '30' },
{ book : 'pencil', '500' : '15'} ]
*/
})
However, if we provide data in the steps as follows, then hashes
can be used. for example:
When the user sets the price for the following items
| item | price |
| book | 500 |
| sharpener | 30 |
| pencil | 15 |
When('the user sets the price for the following items', (dataTable) {
const priceOfItems = dataTable.hashes()
console.log(priceOfItems)
/* It returns :
[ { item : 'book', price : '500' },
{ item : 'sharpener', price : '30' },
{ item : 'pencil', price : '15'} ]
*/
})
### Combination
It is also possible to combine data tables with a scenario outline as well when needed. Let us see an example below:
Scenario Outline: User tries to signup with improper combination of password
Given the user has browsed to the signup page
When the user tries to signup entering the following details
| email | william@xyz.com |
| password | <Password> |
| confirmPassword | <ConfirmPassword> |
Then an error message "<validation>" should be shown above the "password" field
Examples:
| Password | ConfirmPassword | validation |
| 234567569 | 234567569 | This password is entirely numeric |
| 123456789 | 123456789 | This password is too common. |
| abcde | abcde | This password is too short. |
The values in the examples table get substituted into the steps which require the parameter values. The substitution takes place one row at a time. The values from the data table are passed to its step definition at once. For the above example, since there are 3 data rows in the examples table
, this scenario runs thrice with a specific combination of data values as provided in the example table. Let us be clear about how the above example executes.
In the first scenario, a set of data is used from the first row of the examples table, as below.
Given the user has browsed to the signup page
When the user tries to signup entering the following details
| email | william@xyz.com |
| password | 234567569 |
| confirmPassword | 234567569 |
Then an error message "This password is entirely numeric" should be shown above the "password" field
For the step definition of when step, the data table is parsed as follows:
When('the user tries to signup entering the following details, (dataTable) {
const userDetails = dataTable.rowsHash()
/* It returns:
{ email : 'william@xyz.com',
password : '234567569',
confirmPassword : '234567569' }
*/
return client.setValue(emailField, userDetails['email'])
.setValue(passwordField, userDetails['password'])
.setValue(confirmPasswordField, userDetail['confirmPassword'])
.click(signupButton)
})
In the next run of the scenario, the next single row of data is used as follows.
Given the user has browsed to the signup page
When the user tries to signup entering the following details
| email | william@xyz.com |
| password | 123456789 |
| confirmPassword | 123456789 |
Then an error message "This password is too common" should be shown above the "password" field
Similarly, the third run of the scenario uses the third row of the examples table as follows.
Given the user has browsed to the signup page
When the user tries to signup entering the following details
| email | william@xyz.com |
| password | abcde |
| confirmPassword | abcde |
Then an error message "This password is too short" should be shown above the "password" field
Top comments (2)
where did you get the client.setValue from? client is a library? if yes what's the syntax to install via npm?
we can use client. when we use nightwatch-api. Have a look here: nightwatchjs.org/