Cypress, Making Asynchronous Testing Simple

End to End Testing definition

End-to-end testing involves ensuring that the integrated components of an application function as expected. The entire application is tested in a real-world scenario such as communicating with the database, network, hardware and other applications

Essentially E2E testing allows an application to be tested from a user’s perspective simulating a read-world scenario.  E2E allows you to ensure that your application works correctly at a high level.  There are numerous benefits of E2E such as:

  • Increases test coverage
  • Helps to ensure the correctness of the application
  • Helps to detect bugs
  • Reduces costs and time
  • Reduces future risks

There are several different tools that can be used for E2E testing for Angular CLI with Protractor the most commonly used.  Protractor e2e tests are generated with each scaffolded Angular CLI project.  However, an alternative to Protractor is Cypress which is a tool that we have recently used.

Cypress.io

Fast, easy and reliable testing for anything that runs in a browser.

One of the big differences that Cypress offers is that it runs inside the browser and can run as fast at the browser can render content while the other tools execute in a process outside of the browser.  Other benefits include:

  • Snapshots are taken as the test runs. After the test runs these snapshots can be viewed by hovering over the steps in the command log to see exactly what happened at each step.
  • It is possible to debug directly in the developer tools and cypress supplies readable errors and stack traces.
  • Cypress automatically wait for commands and assertions before moving on. There should be no need to write waits and sleeps into the tests.
  • Unit testing functionality, spies, stubs and clocks are available allowing you to control the behaviour of functions, server responses and timers.
  • Access to the XHR objects enabling assertions about its properties.
  • There are screenshots and videos taken when the tests are ran from the CLI.

Cypress Setup

Cypress can be installed through npm, yarn or direct download.  Once installed Cypress can be started in several ways for example directly from the bin folder in node modules e.g. “.node_modules/.bin/cypress open”

Another option is to add an npm script:

cypress

Then from the command line cypress can be opened using

“npm run cypress:open”

‘Open’ will open Cypress in the browser while using ‘Run’ will run all the tests on the command line.

When Cypress opens a GUI is displayed listing the tests along with an option to select the browser you wish to run the tests in

For Typescript projects, the Cypress website has documented how to set up your environment.

Writing Tests

To add a test file, create a new file in the cypress/integration folder and enter the code below.

Cypress

Each test file will contain a describe method which will contain one or more related tests.  This method will contain a string for describing the test and which gets displayed in the browser and a callback function for wrapping the test(s).

Once you save the file the browser with the Cypress tests will automatically reload.

Configuring Cypress

When Cypress is added to a project a cypress.json file is created.  This file can be used to store configuration values to be used for the testing.  Common examples might be:

  • baseUrl
    1. This will prefixed to any url referenced in the cy.request or cy.visit
  • env
    1. any variables to be set as environment variables.
    2. These are used for any values that are different across developer machines
    3. Across multiple environments
    4. Values that change frequently
  • port
    1. port used to host cypress
  • watchForFileChanges
    1. indicates whether cypress will watch and restart tests on test file changes

It is also possible to create a cypress.env.json file which Cypress will automatically check that overwrites any conflicting variables in the main config file, cypress.json.  This is a useful feature as it can be added to the .gitignore file and then used for individual developer settings.

Examples set up of environment variables and retrieving within the test

Cypress
Cypress.env.json

This variable can then be accessed within the test by:

Cypress

Sample Test to fill a Login form

The beforeEach method will be ran prior to each of the tests in this file.  In this instance the ‘testuser’ is being retrieved from the cypress.env.json file.  It will contain the username, password and tenant to be used for the login.  There is also a before method that can be used, which will run once at the start but not before each test.

The first test will test a correct login entering the values into each of the input fields on screen.  Elements on screen can be accessed any number of ways, using the id as the example above, by class, by name etc.  It is also possible to use a data-cy attribute such as data-cy=”txtusername” which can be added to the element and then referenced within the test by cy.get(‘[data-cy= txtusername]’).

Actions Performed in the test

  • visit(‘/’) – The test will navigate to the baseUrl as configured in the cypress.json file.
  • get(‘#tenant’).type(tenant); – This will find the element with an id of tenant and type the value stored within the tenant variable as configured in the beforeEach.
  • Same process is performed for the username and password fields.
    1. If running in the browser the typing is displayed on screen
  • get(‘form’).submit().then() – Get the form element on screen and submit and wait.
  • Within the submit callback function there are additional checks performed
    1. url().should(‘include’, ‘/home’) – This will confirm that after logging in the home page is displayed.
      1. If the url doesn’t include ‘/home’ this test will fail.
    2. window() – Allows the window object to be checked. This is a useful option in Cypress in that in allows access to the DOM.  This would allow test data to be injected in if you needed to test specific behaviour of the application.
      1. In this case after a user has been logged in there should be a local storage token set up for the authenticated user
      2. Expect is used to check that the username matches the username we entered and that the token associated with the logged-on user is not null

The second test is like the first but this one is entering an incorrect password and testing that the system will not log that user in.

Cypress Runner

When the tests run in the Cypress runner a log on the left-hand side will show the step being executed and the browser window is shown on the right-hand pane.  If the tests complete successfully, they will be summarised like below.Cypress

Clicking on one of the tests will expand the list of steps ran (snippet below)

Cypress

Clicking on one of these steps will show the snapshot on the right-hand side.  For example, clicking on the password step displays the following snapshot.

Cypress

Bypass UI

With Cypress it is possible to bypass the UI which is useful when you wish to test a part of the application which is reliant on state from a previous feature. One common example would be where a login is required to reach certain features.  It is not necessary to manually login into the application before testing each feature of the application.

 

cy.request() automatically gets and sets cookies and can be used to build up state without having to use the UI but importantly still perform the same as if it had come from the browser.  The following code demonstrates how a user can be logged in with a token and user role assigned.

Cypress

Additional Cypress Commands

There are many commands that can be used in Cypress, a full list of which are available here.  However, there are a couple of additional commands not previously shown which are worth highlighting.

  • route()
    1. This can be used to manage the behaviour of network requests or do interrogate the response of the request. Currently it only supports XMLHttpRequests.

Cypres

In the above example, we are testing a button on screen which allows the user to add a comment relating to the page that they on.  Cy.route() is defining the method type, url and giving it a name.  The button click is then simulating and cy.wait then waits for the XHR.  When the response is received there are several properties that can be checked

  • Status
  • Method
  • Responsebody
    • In this checking the response has a property
    • And that property (testVM) has a property with a value.

 

  • contains()
    1. Get the DOM element containing the text
    2. The element can contain more than the desired text and the test will still pass.

Custom Cypress Commands

It is possible to write custom commands and to overwrite existing commands.  The recommended place to create these commands is in the commands.js (or commands.ts if using typescript) located in cypress/support/commands.js.  This file is loaded prior to any test files being evaluated.

The syntax is Cypress.Commands.add(name, callbackFuntion).  If overwriting an existing command, then use overwrite instead of add.

To stay up to date with Dataworks Limited news and events, connect with us via the links below :