Cucumber, Gherkin and readability
The way they set up the environment to allow for these end-to-end tests creates a new form of improvements to the so-known end-to-end testing. First, Pau and Carlos will talk about the different parts that lay the foundation for this kind of implementation, and second, they will look at how this works in the bigger picture.
Cucumber is one of the popular tools for behaviour-driven development (BDD) in software engineering, as it allows developers to express the behaviour of a system in a natural language. This makes it easier for non-technical stakeholders to understand. This language is called Gherkin. A good example for this would be:
Feature: Search for a product
As a user
I want to search for a product
So that I can find the product I am looking for
Scenario: Successful search
Given I am on the homepage
When I search for ‘cucumber’
Then I should see a list of products related to ‘cucumber’
Here, we are defining a test case to check for a successful search of a product in a given website. As shown, the readability of a test case written in Gherkin is greatly improved on compared to any common Cypress programming language. This is because it is written in a natural way and thereby enables anyone to understand what is supposed to happen in this case.
Cypress and the integration
Cypress is an end-to-end testing framework that is often used in conjunction with Cucumber to test the behaviour of web applications. Anyone that is familiar with end-to-end testing web applications knows Cypress and it’ s great capabilities. It also known for it’ s great readability of test code, but we are taking this to the next level.
The integration we are talking about is **@badeball/Cypress-cucumber-preprocessor (**https://github.com/badeball/cypress-cucumber-preprocessor**)**, a tool that allows developers to use Cucumber’s natural language syntax to write Cypress tests. It can be installed via npm and works both for TypeScript and JavaScript based Cypress environments.
To use the preprocessor, you will need to create a “*.feature” file for each test batch., This should contain a description of the behaviour you want to test, written in Cucumber’s Gherkin syntax, as shown on Fig.1.1. This defines the steps needed to test the given scenario.
Next the behaviour should be defined in a “*.steps.js” file. This file needs to integrate the library and must define each step individually, thereby establishing Cypress behaviour for each of the phrases. For the “.feature” file shown in the example above, an example of the implementation would look like this:
import { Given, When, Then } from ‘cypress-cucumber-preprocessor/steps’;
Given(‘I am on the homepage’, () => {
cy.visit(‘http://localhost:3000’);
});
When(‘I search for {string}’, (searchTerm) => {
cy.get(‘input[name=”search”]’).type(searchTerm);
cy.get(‘button[type=”submit”]’).click();
});
Then(‘I should see a list of products related to {string}’, (searchTerm) => {
cy.get(‘.product-list’).should(‘contain’, searchTerm);
});
Now, we can execute the .feature in Fig 1.1, as we now have linked the natural language with something that Cypress can execute. Note that the second and third definition even have an input field, that allows for variables to be defined in the natural language, thus making it simpler to extend this test further. We will explore this in the next paragraph.
How this makes sense
We now know which tools are used and how these are combined. So, let’s get into the nitty-gritty and look at some examples of how this is can be useful and the extra functionalities that make test automation development even easier.
Let’s say we are checking the field verification on a registration page, and we want to check every rejection case for this given form. We could write a test that could look like this:
Feature: Registration form
Background:
Given I go to the main page
And I click on register
Then I should be on the register page
Scenario: Check field acceptance
Given I am on the registration page
When I type ‘<Input>’ in field ‘<Field Name>’
Then I should see an error ‘<Error Message>’
Example:
| Input | Field Name | Error message |
| 12345 | Password | This is a top-10 password |
| N4m3 | Username | Username can’t have numbers |
…
Here, we introduce two interesting new functionalities given by the preprocessor.
First, we are using the ‘Background’ functionality, which will execute the steps defined within, before each Scenario is executed. Background Scenarios define a given context that is relevant while testing the specified page, as this is considered a good practice while developing end to end tests in Cypress.
Second, we have the ‘Example’ field, that will make executing recurrent tests easier, it does so by allowing one to define different inputs.
Each row will represent a different test execution using the same steps with different content and expected results. As it is only a matter of adding rows to the examples to expand the test coverage this makes extending tests very easy.
This also allows for modular behaviour in the methods, as the test is defined ultimately in the “*.feature” file, thereby enabling us to use methods on our pages that make it possible to leverage different behaviours given the input. Usually This is considered as a bad practice while testing, since permitting different routes for a test often defeats the purpose of said test, but when this is used just to create steps in Cucumber that are more flexible and readable, we can reduce the overall coding effort.
The format for the ‘Example’ sentence can also be used as parameters for a Give/When/Then/And sentence. For the former example, we could write it like this:
Feature: Registration form
Background:
Given I go to the main page
And I click on register
Then I should be on the register page
Scenario: Check field acceptance
Given I am on the registration page
When I type in the registration form
| Input | FieldName |
| 12345 | Password |
| 123456 | PasswordCheck |
Then I should see an error ‘Passwords do not match each other’
For this to work, we would need to add the parameters to the “*.steps.js” for the ‘I type in the registration form’ step, looking something like this:
import { Given, When, Then, DataTable } from ‘cypress-cucumber-preprocessor/steps’;
…
When(‘I type in the registration form’, (datatable) => {
datatable.hashes().forEach((element) => {
cy.get(‘[data-cy=’+ element.FieldName + ‘]’).type(element.Input);
})
});
…
As it is shown, the DataTable allows us to get arrays straight from the Cucumber code, so writing code that would otherwise have to be repeated over and over, becomes filling rows on a table in a very straightforward and readable way. It allows for different behaviours based on the given parameters while keeping the good practices at the same time, as the ‘real’ test procedures come from the Cucumber definitions.
Conclusion
In conclusion, Cypress-cucumber-preprocessor is a useful package that allows developers to use Cucumber’s natural language syntax to write Cypress tests. This makes it easier for non-technical stakeholders to understand the behaviour of a system, while also reducing the deve