Achieve Behavior Driven Development With Tests Using Cypress and Gherkin

📅March 02, 2023

🕒7 minute read

🏷

testing

cypress

gherkin

bdd

tdd

In this article, we'll explore how to up your testing to the next level with a more streamlined and collaborative method: Behavior Driven Development (BDD).

Just like pretty much any other question related to software engineering, the answer to the "How to test an app?" question is depends, and with so many tools at hand, lots of new methods have risen and given hope to the headache of traditional testing methods.

Behavior Driven Development (BDD)

BDD is based on the idea that the behavior of the software should be the primary concern of all stakeholders (developers, testers, managers, etc). It encourages them to work together to define the behavior of the software in a clear, concise, and unambiguous language that is easily understandable by everyone.

It's essentially TDD (test driven development) but high-level and focused on user interactions.

In BDD, the behavior of the software is expressed as user scenarios, which describe the interaction between a user and the system in a specific context.

Scenarios are written in a structured human-readable format, often using the Gherkin syntax, which makes them easily readable by both technical and non-technical people.

For example, a feature file for login:

Feature: Login functionality

  Scenario: User logs in with valid credentials
    Given I am on the "/login" page
    When I enter my email and password
    And I click the login button
    Then I should see the "/home" page

  Scenario: User logs in with invalid credentials
    Given I am on the "/login" page
    When I enter invalid email and password
    And I click the login button
    Then I should see an error message

Yeah, it leaves out a lot of technical details, but those don't really matter when discussing something as high level and user-relevant as login.

On a low-level, it involves a combination of unit and integration tests, and thats what the devs and testers are for. This ambiguity is a trade-off. We now have a common platform for everyone working on a feature to define acceptance criteria.

This paves the way for a very manageable approach to cross-team communication when designing new products/features, which is a problem at many companies, especially when teams are a mix of devs/testers and non-technical individuals.

Now, combine all of this with Cypress, and we have end-to-end (E2E) testing 🥳

Cypress and Gherkin are a powerful combination.

Cypress is a popular E2E testing framework for web apps. It provides an easy-to-use API for writing tests and includes features such as automatic waiting, real-time reloading, and debugging.

One way to write Cypress tests is to use the Gherkin syntax. Its used in conjunction with the Cucumber testing framework to automate acceptance tests.

To use Gherkin syntax with Cypress, you need the cypress-cucumber-preprocessor package. This library allows you to use Gherkin syntax with Cypress by preprocessing the feature files and converting them into Cypress tests.

npm install --save-dev cypress-cucumber-preprocessor

Once the package is installed, feature files (like the example above) with .feature extension can be created in the cypress/integration directory.

Writing Tests

Feature files can and should be written collaboratively (usually in team planning meetings).

Once you have written your feature file, the steps must be defined in Cypress. Step definitions are JavaScript functions that map each step in the feature file to a Cypress command.

Using the example Login feature file mentioned above, tests can look like this:

const { Given, When, Then } = require("cypress-cucumber-preprocessor/steps");

Given("I am on the "/login" page", () => {
  cy.visit("/login");
});

When("I enter my email and password", () => {
  cy.get("#email").type("user@test.com");
  cy.get("#password").type("trustno1");
});

When("I enter invalid email and password", () => {
  cy.get("#email").type("user@test.com");
  cy.get("#password").type("invalidpassword");
});

When("I click the login button", () => {
  cy.get("button[type='submit']").click();
});

Then("I should see the "/home" page", () => {
  cy.url().should("include", "/home");
});

Then("I should see an error message", () => {
  cy.get(".error").should("be.visible");
});

Conclusion

Writing E2E tests in general doesn't mean you should stop writing unit, ui or integration tests, but E2E in this format helps with the load of tests needed and since it's testing user scenarios, it ensures more reliability in your system for the big features.

Like I mentioned above, the ambiguity we have by using BDD is a trade-off and should be discussed between teams before even considering adopting. It works in collaborative and product-focused teams and it worked on one of my teams.