A step-by-step tutorial showing a Cypress test refactoring.
Recently a user posted on LinkedIn and Twitter a Cypress test that checks each card element on the page against the data object. Here is the posted code screenshot.
In this blog post I will show how to refactor the above test to make it simpler and more accurate. There are hidden problems in the test code that might make it pass accidentally. We want to avoid such false positives, since they destroy the confidence in the ability of our automated tests to discover problems.
🎁 You can find the source code in the recipe "Check Cards" hosted on my Cypress examples site. You can watch me refactoring the code and explaining how I go about it in the video Check Cards Test Refactoring 📺.
The initial code
Let's take the following HTML fragment to be our dummy "app".
The test passes, all is green and good in the world.
Refactor the code
Let's shorten the code. We don't have to use cy.wrap($el) multiple times. We can use the expect($el).to.include.text(...) assertion from Chai-jQuery library to verify the text is present.
Since the page is static by the time we start checking, the expect(...).to... assertions work just fine. Alternatively, we can use cy.wrap($el).within(() => ...) commands to limit the search to the element.
There is a problem with out test. If you have over individual contains commands in the left Command Log column, you can see the element if finds. Oops, seems we tried to find cy.contains('4') and accidentally found 1 even in the last 24 hours. Not what we expected to find.
I really dislike using cy.contains(partial text) command, and instead prefer cy.contains(selector, partial text). Let's add data-cy attributes to our HTML markup to be able to precisely find elements.
What if our page rendering is incorrect and it only renders the first card? No problem, the test passes!
Remember: you cannot trust the page, but you can trust the data. Thus you should iterate over the data list of projects and check each item against what is on the page.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
// confirm the correct number of items is shown cy.get('main li.card').should('have.length', mockProjects.length) // iterate over the data to confirm the info // for each card is shown correctly mockProjects.forEach((project, index) => { const { name, numIssues, numEvents24h, status } = project cy.get('main li.card') .eq(index) .within(() => { cy.contains('[data-cy=name]', name) cy.contains('[data-cy=issues]', `${numIssues} issues`) cy.contains('[data-cy=24h]', numEvents24h) cy.contains('[data-cy=status]', status) cy.contains('a[href="/dashboard/issues"]', 'issues') }) })
You can get the data the page is derived from by observing network traffic using the cy.intercept command or by accessing the data in the application.