Categories
Programming

Cypress E2E testing best practices + gotchas

Cypress E2E testing best practices + gotchas

Cypress is a new testing framework that we just moved to, so it is common to have growing pains and learning the ins and outs as we work with it more. Here are some common practices that we found to prevent any flakiness in the tests.

  • Always clean up any entities in the test suite before and after the test suite

E.g in the `beforeEach() or before()`

before(() => {

   cy.serviceVersion(‘DELETE’, ‘service-version-new’).then(() => {

     cy.servicePackage(‘DELETE’, ‘service-package-new’)

   })

 })

Also make sure to delete it in the `afterEach() or after()` as well.

afterEach(() => {

   cy.serviceVersion(‘DELETE’, ‘service-version-new’).then(() => {

     cy.servicePackage(‘DELETE’, ‘service-package-new’)

   })

 })

This will minimize any lingering entities and make the tests more independent and deterministic

  • Make sure the element you are selecting has a unique selector

Cypress will error if it finds multiple elements selected, so make sure that the element that you are selecting has a unique data-testid or selector so that it doesn’t potentially return more than one value.

       cy.get(‘[data-testid=”i-am-very-unique”]’).click()

  • Make sure you assert on elements that are unique to the page you are testing

This goes in hand with point 2, but the Cypress test environment is very eager so as soon as it finds an element matching the selector, it will act on it, even if it hasn’t navigated to the proper page yet – it will attempt to assert it on the current page if there is an matching element. So make sure the selector is unique to the page you are testing.

     cy.get(‘[data-testid=”i-only-exist-on-the-page-you-want-to-test”]’).click()

  • Be careful of using `.contains()` – it may not work the way you expect it to

In cypress, .contains() will yield a DOM element which you can chain other commands to, but it will operate under the scope of that selected DOM element. This can cause undesired assertions if we are inadvertently chaining a lot of assertions. If we just want to make a straight assertion on the DOM, we should use .should(‘contain’) instead

cy.get(‘foo’).contains(‘bar’) // everything chained after this will operate under the foo element that contained bar

cy.get(‘foo’).should(‘contain’, ‘bar’) // a straight assertion that doesn’t yield unexpected side effects when chaining

  • Try to assert on an element on the page before navigating or clicking somewhere

In Cypress, it will eagerly try to click or navigate as soon as it finds the element on the page before data is fully rendered – this leads to unexpected consequences. Instead of a .wait we can also assert on something in the page which will cause Cypress to wait until that element loads before the next step. 

cy.get(‘[data-testid=”Plugin-card”] .empty-state-content’).should(‘exist’) // this causes Cypress to wait until the KEmptyState has loaded which was a result of an XHR request returning an empty dataset. This ensures that Cypress waited for that request to finish before moving to next step.

cy.get(‘[data-testid=”entity-button”]’).click() // and then click on the entity button. Had we not done the previous assertion, Cypress would have clicked this immediately and any props or params that we got from any XHR requests would not have been set correctly.