React E2E Testing made easy using Cypress and Jenkins
Tags: react, frontend, testing, cypress
Published: 2021-09-22 06:50:19

#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. 

![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/i9fyph18vjmwcfbk1tvo.png)
 

#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 gives 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 finished 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 integrated step in our CI Pipeline in e.g. Jenkins, Azure Pipeline,... 

##Run Cypress in Dev Environment

To execute Cypress with an 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. 

![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2guukogff1r2oebv7fgk.png)
 

#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
```