Cypress code review checklist and best practices
Code Review/ Cypress best practices
We can follow some of the below practices during code review and as a best practice when we write any new test case.
- Unwanted waits.
For any framework/tool, adding wait is an unwanted wait and not a best practice. Cypress automatically waits for all commands and assertions to pass/fail before moving to the next step. But, for complex scenarios, while handling date-pickers or dropdowns, etc., we can use the below timeouts.
Explicit Timeout — Time, in milliseconds, to wait for a specific element to load in the DOM.
cy.get(test).type(‘name’, {timeout: 3000};
cy.get(test).type(‘name’, {delay: 1000};
PageLoadTimeout — Time, in milliseconds, to wait for page transition events or cy.visit(), cy.go() commands to fire their page load events.
We can initialize PageLoadTimeout in `cypress.json` file. { “PageLoadTimeout
20000 }
ResponseTimeout — Time, in milliseconds, to wait for a response in commands such as `cy.request(), cy.fixture(), cy.getCookies()`, etc., { “responseTimeout” : 10000 }
- Chaining Assertions.
It’s always better to add multiple assertions to a single command instead of multiple lines of code. Assertions in multiple lines of code slower the test case.we can use like below
it(‘validates and formats first name’, () => {
cy.get(‘#first’) .type(‘johnny’)
.should(‘have.attr’, ‘data-validation’, ‘required’)
.and(‘have.class’, ‘active’)
.and(‘have.value’, ‘Johnny’) })
- Global baseurl setup.
Baseurl should be set up in cypress.json file instead of passing the URL to commands like cy.visit(“https://www.test.com/”)
{
“baseurl” : “https://www.test.com"
}
- Each test must be runnable in complete isolation
Tests should always be able to be run independently from one another and still pass.
Tests can also fail at any step. Each it block is run regardless of previously run it blocks. If one it block relies on the state left by another it block, cascading failures can happen that are very difficult to understand. Use before to run once per describe block and beforeEach to run once per nested it blocks.
- How we verify test can run as isolation
You only need to do one thing to know whether you’ve coupled your tests incorrectly, or if one test is relying on the state of a previous one. Put an .only on the test and refresh the browser. If this test can run by itself and pass — congratulations you have written a good test
- Combine multiple tests into one larger test
Let’s imagine the following test that is filling out the form.
describe(‘my form’, () => {
it(‘visits the form’, () =>
{ cy.visit(‘/users/new’)
})
it(‘requires first name’, () =>
{ cy.get(‘#first’).type(‘Johnny’) })
it(‘requires last name’, () =>
{ cy.get(‘#last’).type(‘Appleseed’) })
it(‘can submit a valid form’, () =>
{cy.get(‘form’).submit()
})})
What’s wrong with the above tests?
They are all coupled together! If you were to put an .only on any of the last three tests, they would fail. Each test requires the previous to run in a specific order in order to pass.
Here’s 2 ways we can fix this:
- Combine into one test
- Run shared code before each test
- Selecting Elements
Use data-* attributes to provide context to your selectors and isolate them from CSS or JS changes.
Every test you write will include selectors for elements. To save yourself a lot of headaches, you should write selectors that are resilient to changes.
<button id=”main” class=”btn btn-large” name=”submission”
role=”button” data-cy=”submit”>Submit</button>
How we can target it:
- Reusable functions.
Automation is more about reusability & `Do not reinvent the wheel` principle. While preparing scripts, if we face any duplicate code or function, then it’s always good practice to create a reusable function for that and call in other scripts when required to minimize the code duplication. “Write once — Call multiple times”…
Use commands.json for reusable functions.
- Test data
To create & maintain test data, we can use external sources such as Excel, CSV, and JSON files. But the best way is to store data in .json and using cy.fixture() we can call this .json file in our scripts to reduce the data duplication & maintenance.
Create a JSON file and store data “user.json”
e.g
cy.fixture(‘user’).then((data) =>
{ data.firstName = ‘Jane’
})