How to spit a single Cypress test into several smaller isolated tests.
In this blog post I will continue working with my solution to the Test.Bash 2022 UI Automation challenge. I have described and recorded a video of my solution in the blog post Solving Test.Bash 2022 UI Challenge With Cypress. In this blog post, I will try to split the single test into a few shorter isolated tests that show a few tricks I normally employ in my Cypress testing.
The test as written for the challenge entered the message like a user, then logged into the system as an admin, then viewed the message. So the test did several things together:
cypress/e2e/spec.cy.js
1 2 3 4 5
it('sends a message successfully', { retries: 2 }, () => { // enter the random message // log in as the admin // check the message })
Our tests mostly accessed the elements using the data-testid attributes.
The repetition of the data attributes is a little too verbose. Let's add a custom Cypress command. Since we are getting an element by its test id, I will call my custom command cy.got
Our single test is too long - the web application can reset during the test and the might fail. We got around it by using test retries, but let's make it better. Let's test each feature separately: submit a form, log in, view messages. To confirm the user can submit the form, we can enter the form, and observe the network call to ensure the backend accepts the form submission. We can use cy.intercept command to spy on the call.
Logging in using the UI works, so now let's test a faster way that other tests could use - logging in using the API call, just like the login page does. We can use the cy.request command.
1 2 3 4 5 6 7 8
it('can log in using API call', () => { cy.request('POST', '/auth/login', { username: Cypress.env('username'), password: Cypress.env('password'), }) cy.visit('/#/admin/messages') cy.contains('a', 'Logout').should('be.visible').scrollIntoView() })
it('can log in using API call', () => { cy.login() cy.visit('/#/admin/messages') cy.contains('a', 'Logout').should('be.visible').scrollIntoView() })
View messages
Now that we can quickly log in, let's check if the admin user can see the posted messages. We will send a new message using a cy.request command - which is the same network call as the test "sends a message successfully" shows works.
cy.get('.row.detail') .should('have.length.greaterThan', 0) .contains('.row', subject) .should('have.class', 'read-false') .click() cy.contains('.message-modal', subject) .should('be.visible') .and('include.text', name) .and('include.text', phone) .and('include.text', email) .and('include.text', subject) .and('include.text', description) .contains('button', 'Close') .wait(1000) // just to make the video clear .click() cy.contains('.message-modal', subject).should('not.exist') cy.contains('.row.detail', subject) .should('have.class', 'read-true') .and('not.have.class', 'read-false')
After this refactoring, each test is short and independent from each other. If we need to add more tests, we can use the existing tests as the starting point.