Skip to content

Using Cypress with Cucumber in a React Application

Using Cypress with Cucumber in a React Application

Introduction

We are going to be learning how to integrate Cypress with Cucumber in a React application. Cypress is the premier end-to-end testing platform to allow you to test user interactions, and end-to-end flows in your application.

What is Cucumber?

Cucumber logo

Cucumber creates a layer of abstraction that allows for better communication between business and technical folks, and facilitates the use of Behavior Driven Development (BDD). One of the additional benefits is that it essentially creates documentation, which stays up to date and is easy to share. Cucumber is built to support Behavior Drive Development (BDD). If you are unfamiliar with Behavior Driven Development, you can read Cucumber’s guide on it here: https://cucumber.io/docs/bdd/

Learn more about Cucumber in general here: https://cucumber.io/docs/guides/overview/

What is Gherkin?

In the simplest terms, Gherkin is a set of grammar rules that structure plain text for Cucumber to understand and process.

Learn more here: https://cucumber.io/docs/gherkin/

Setup

Steps

- Clone a React app to test against. Here is a link to a photo viewer and search app which we will be using:

https://github.com/Yog9/SnapShot

You will need to clone the app and get it running before we can move on to next steps. Below is a screenshot of the working app:

SnapShot app screenshot

- Add Cypress as a dev dependency within your application. Install Cypress npm i cypress --save-dev. Once you have completed this step, you should see "cypress": "^10.11.0", or something similar in the devDependencies section of your package.json file.

- Open and run Cypress to install it using the binary. You do this by typing the command npx cypress open in your terminal. If it's your first time running this command within your application, Cypress will prompt you to install some example tests and other helper files, and guide you through a basic configuration necessary for Cypress to operate within your application.

Cypress 1
Cypress 2
Cypress 3
Cypress 4
Cypress 5
Cypress 6

- Add the cypress-cucumber-preprocessor package to your project. This package was originally maintained by BrainFamily, but was recently taken over by Badeball. You can find it here:

https://www.npmjs.com/package/@badeball/cypress-cucumber-preprocessor

Use the command npm install --save-dev @badeball/cypress-cucumber-preprocessor

You can find the quick start guide here:

https://github.com/badeball/cypress-cucumber-preprocessor/blob/a2702f5ce247c96269a0432358ed919b596a4dbb/docs/quick-start.md

- Add @bahmutov/cypress-esbuild-preprocessor esbuild https://github.com/bahmutov/cypress-esbuild-preprocessor. Use the command npm i -D @bahmutov/cypress-esbuild-preprocessor esbuild. This should result in "esbuild": "^0.15.13" as well as "@bahmutov/cypress-esbuild-preprocessor": "^2.1.5" becoming part of the devDependencies in your package.json file.

Add cypress/webpack-preprocessor to your project. Use the command npm i --save-dev @cypress/webpack-preprocessor to add this package. The devDependencies section of your package.json file should now include, "@cypress/webpack-preprocessor": "^5.15.3" if done correctly.

- Configure cypress.config.ts file. Configure specPattern with "**/*.feature" and setupNodeEvents. Here is my file:

const { defineConfig } = require("cypress");
const createBundler = require("@bahmutov/cypress-esbuild-preprocessor");
const addCucumberPreprocessorPlugin =
  require("@badeball/cypress-cucumber-preprocessor").addCucumberPreprocessorPlugin;
const createEsbuildPlugin =
  require("@badeball/cypress-cucumber-preprocessor/esbuild").createEsbuildPlugin;

module.exports = defineConfig({
  e2e: {
    async setupNodeEvents(on, config) {
      const bundler = createBundler({
        plugins: [createEsbuildPlugin(config)],
      });

      on("file:preprocessor", bundler);
      await addCucumberPreprocessorPlugin(on, config);

      return config;
    },
    specPattern: "cypress/e2e/features/*.feature",
    baseUrl: "http://localhost:3000/SnapShot#/SnapScout/",
    chromeWebSecurity: false,
  },
});

Additionally, you will want to generate a cypress-cucumber-preprocessorrc.json file at the root level of your project in order to handle some additional configuration settings. My file contents are below:

{
  "json": {
    "enabled": false,
    "output": "jsonlogs/log.json",
    "formatter": "cucumber-json-formatter.exe"
  },
  "messages": {
    "enabled": false,
    "output": "jsonlogs/messages.ndjson"
  },
  "html": {
    "enabled": false
  },
  "stepDefinitions": [
    "[filepath]/**/*.{js,ts}",
    "[filepath].{js,ts}",
    "cypress/e2e/step_definitions/*.{js,ts}",
    "[filepath]\\***.{js,ts}",
    "[filepath].{js,ts}",
    "cypress\\e2e\\step_definitions\\*.{js,ts}"
  ]
}

Organize your files. There are multiple ways to organize your feature files and step definition files. For the purposes of this tutorial, we will place our feature files in a cypress > e2e > features directory, and our step definition files in a cypress > e2e > step_definitions directory.

Let's Write Some Tests!!

Let's create a simple feature file with some basic scenarios and test steps for this application. Here is a start in app.feature!

Feature: Snapshot website test scenarios
  Scenario: visiting the home page - successful search
    Given I visit the home page
    Then the header should be visible
    Then I click in the text input
    Then I type birds into the search input and press search button
    Then I should see Birds Images in header
    Then I click the Food button and see Food header and 24 images
import { Given, Then } from "@badeball/cypress-cucumber-preprocessor";

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

Then("the header should be visible", () => {
  cy.get("h1").should("be.visible");
});

Then("I click in the text input", () => {
  cy.get("input").click();
});

Then("I type birds into the search input and press search button", () => {
  cy.get("input").type("birds");
  cy.get(".search-button").click();
});

Then("I should see Birds Images in header", () => {
  cy.get(":nth-child(24) > img").should("be.visible");
});

Then("I click the Food button and see Food header and 24 images", () => {
  cy.get(":nth-child(4) > a").click({ force: true });
  cy.get(":nth-child(24) > img").should("be.visible");
});
Cypress test list screen
Running Test Screen

Summary

As you can see, setting up Cypress with Cucumber is a fairly straight-forward process. There are incredible benefits to using Cucumber with Cypress, particularly related to facilitating communication between non-technical and technical members of a development team, as well as creating ongoing project documentation, and an easy-to-understand guide for what your application or feature is doing.