DEV Community

Committed Software
Committed Software

Posted on • Originally published at committed.software on

Cypress.io - A JavaScript E2E Testing Framework

cypress logo full

End to end (E2E) testing is a crucial part of any development cycle and until now, E2E UI testing has been primarily done using Selenium WebDriver. Now though, a new testing framework, Cypress, is available. It differs from Selenium and offers several extremely useful game-changing features, such as:

  • Asynchronous DOM element finding
  • Server stubbing
  • ‘Time Travel’ debugging

Setup

Setup could not be simpler - Cypress is just an npm package, and so can be installed as a package and run interactively with cypress open, or run in headless mode with cypress run

Asynchronous by design

Anyone who has any experience writing Selenium tests will likely know the pain of synchronisation: dealing with the problem of tests running before certain elements have loaded into the DOM. Cypress solves this by using JavaScript promises. Manually writing an implicit or explicit wait, as was the case in Selenium, is no longer needed as implicit waits are effectively built in.

describe('a sample test', () => {
  it('should do stuff', () => {

    cy.visit(appUrl) // Go to appUrl

    cy.get('.some-button').click() // Wait for .some-button to be present, then click on it

    cy.get('.some-other-button', ($btn) => {
      // do stuff with the element $btn
    })

    cy.get('.yet-another-button', ($btn) => {
      // do stuff with the element $btn
    }, {timeout: 60000}) // If Cypress doesn't find the element within 60000 ms, then error
  })
})

Server stubbing

Cypress has built-in functionality to stub/mock any backend servers linked to the UI. This has 2 major advantages:

  • We can remove any dependencies on a backend by stubbing responses for every request made to the API.
  • We can mock some responses - for example, login attempts, or 3rd party APIs that we don’t want our tests to depend on.
describe('another sample test', () => {

  beforeEach(() => {
    cy.server() // Enable response stubbing
    cy.route({
      method: 'GET', // when the app sends a GET request
      url: 'http://www.some-api.com/something', // to this URL
      response: {message: 'Some mocked message'} // then return this response
    })

    cy.visit(appUrl)
  })

  // Tests...
})

Time travel debugging

Even with a trivial setup and built-in support to tackle synchronisation issues, Cypress has another gem for developers. Interactive, ‘time travel’ debugging. In a simple UI, the user can see all the steps in the tests being run and see what happened before, during, and after each step.

Continuous Integration

Continuous Integration can often be tricky when it comes to E2E testing. In terms of running Cypress tests, this is simple - it’s just an npm command. The potential difficulty comes in the form of running a UI (and maybe an API) as part of the pipeline. We achieved this by spinning up instances of the UI and backend as Docker containers using Docker Compose, although this is obviously dependant on a project’s infrastructure and deployment methodology.

Conclusion

Cypress has amazing potential as a UI testing solution. Not only is it easy to get up and running, it also solves one of Selenium’s biggest headaches - synchronisation. By doing this using promises, getting used to this should be easy for most JavaScript developers. Time travel debugging allows for easy insight into where and why tests are failing, and the ability to stub part or all of the backend allows for testing just the UI, an entire system, or a mix of both.

Top comments (0)