Using Cypress and Jenkins for React E2E Testing

Written by jochenrui | Published 2021/09/24
Tech Story Tags: react | cypress | testing | jenkins | end-to-end-testing | blackbox-testing | software-development | what-is-e2e-testing

TLDREnd-To-End(E2E) Testing is to test the Application from the user's perspective. E2E Tests are written in a way that resembles the User's way of operating our Application. They help catch errors early and debug them, thus increasing the pace of development. In the example above we intercept Http Requests by defining the endpoint and mocked response. We can run the tests without starting a backend instance and see if our application works as intended. For this example, we can choose to run with a UI and open a chrome instance for easier debugging.via the TL;DR App

What is End-To-End(E2E) Testing?

The primary goal of E2E Testing is to test the application from the user's perspective - thus regarding the application as a black box, ignoring the internal logic, and only testing what the users see.

Drawbacks of E2E Testing

An error in the E2E Test Suite indicates that the user can't use the application as intended. The problem is that we can't pinpoint the exact Line Of Code(LOC) that causes the error. Thus E2E Testing helps in finding significant errors but can't help in debugging them.

On the famous Testing Pyramid, E2E Tests can be found on top of Component and Integration Tests. As such, there should be Unit and Integration Tests first. These help in catching errors early and debugging, thus increasing the pace of development.

Benefits of E2E Testing

E2E Tests are written in a way that resembles the user's way of operating our application. As such E2E Tests give us great confidence in our application by confirming that the key functionalities are working as intended from the user's point of view.

In addition to this, E2E Tests ideally don't rely on Implementation Details; as such, they are more robust and written in a way where fixing or updating them is fast and easy.

Practical Example

Now to the fun part: Code!

First, we have to install Cypress.

npm install cypress --save-dev
or
yarn add cypress --dev

Then we can create a simple cypress.json configfile in the root directory.

{
    // specify the baseUrl from which we 
    // serve our applications in the test environment
    "baseUrl": "http://localhost:3000",

    // depends on project: allows accessing shadow dom without calling .shadow()
    "includeShadowDom": true,

    // optional: only necessary cypress component testing
    // not needed if all we do is e2e testing 
    "component": {
        "testFiles": "**/*.spec.{js,ts,jsx,tsx}",
        "componentFolder": "src"
    },
}

If our project is written in typescript, we might want to add a tsconfig in the cypress subdirectory that extends our main tsconfig.

cypress/tsconfig.json

{
  "compilerOptions": { "types": ["cypress"] },
  "extends": "../tsconfig.json",
  "include": ["integration/*.ts", "support/*.ts", "../node_modules/cypress"]
}

Writing Tests

After we finish the basic setup and installation, we can now start writing tests.

describe("Sample Test Suite", () => {
  beforeEach(() => {
    // intercept outgoing HTTP Requests by defining the endpoint and mocked response
    cy.intercept("GET", "/some_endpoint", {
      statusCode: 200,
      body: {"a":1},
    });
  });

  it("sample test", () => {
    // uses baseUrl defined in cypress.json configuration
    cy.visit("/landing-page");
    // access DOM Nodes via e.g. class, id, data-test-id
    // & interact with DOM
    cy.get('[data-test-id="add-button"]').click();
    cy.get(".some_class").should("exist");
  });
});

In the example above, we intercept Http Requests our application makes to the /some_endpoint endpoint. Thus we mock the backend and can run our tests without starting up a backend instance.

Now we can run the tests and see if our application works as intended. For this, we can choose to run it with a UI and open chrome instance for easier debugging, OR we can run it headless, e.g., for a quick run in CLI or as an integrated step in our CI Pipeline in Jenkins or Azure Pipeline, etc.

Run Cypress in Dev Environment

To execute Cypress with a UI and controlled Chrome instance, we can add this script to package.json

"cy:open": "node_modules/.bin/cypress open",

Adding this allows us to easily start the cypress UI in the terminal.

npm run cy:open

Jenkins Integration

To integrate Cypress into our Jenkins Pipeline, we can add these scripts to package.json

"cy:run": "node_modules/.bin/cypress run",
"ci:e2e": "start-server-and-test start http://localhost:3000 cy:run"

In addition, we need to install start-server-and-test for this solution.

npm install --save-dev start-server-and-test

This will ensure that our server is started before we try running our E2E Tests.

Now that all the preparations are done, we can add a step to our Jenkinsfile.

sh script: 'cd frontend; npm run ci:e2e'

Now when a Jenkins Build is triggered, we will see a new stage in our Pipeline that displays a report of our E2E Tests.

Additional Information and Troubleshooting:

Depending on the Docker Image used, we may need to install additional OS-specific dependencies. For this, we can add a DockerFile step:

# Install cypress OS dependencies
RUN apt-get install -qy \
    libgtk2.0-0 libgtk-3-0 libgbm-dev libnotify-dev libgconf-2-4  \
    libnss3 libxss1 libasound2 libxtst6 xauth xvfb procps

This article was first published at: https://dev.to/jochen/react-e2e-testing-made-easy-using-cypress-and-jenkins-42jm.


Written by jochenrui | Fullstack Dev (JS, TS, Python, Java)
Published by HackerNoon on 2021/09/24