On July 31, 2021, I had the pleasure of participating in the Minas Testing Conference, where I spoke about How to create fast and robust automated tests with Cypress.
Note: The lecture was in Portuguese.
The talk was really cool, and I brought you a summary with each of the best practices, so you can apply it to your projects and make your tests faster and more robust.
Note 2: You can apply most of the best practices mentioned below regardless of the test framework being used, but some are specific to Cypress.
1. Independent tests
Automated tests must run in isolation, without the need for another test to run to create some state in the application under test.
The failure of one test shouldn't impact the results of other tests.
In addition, independent tests are easier to parallelize.
Tip: use the
beforeEach hook when you need to perform repeated steps for all tests of a given
2. Programmatic authentication
Logging in via the graphical user interface as a pre-condition for all tests is costly (in terms of execution time), and it makes the tests dependent on each other, which is a bad practice.
A disabled login button due to an HTML error, for example, shouldn't break an entire test suite.
By adding mechanisms to log in programmatically, you make tests faster and more independent.
3. State creation mechanisms
By creating such mechanisms, we ensure that tests are completely decoupled from each other, we don't need abstractions that add complexity to tests, such as Page Objects, and we guarantee fast and straight-to-the-point tests.
Examples of these mechanisms are API calls for creating resources, communication with the database via tasks, or running scripts at the operating system level.
Adding attributes to front-end elements, such as
data-cy, adds to the application what I call "testability," as these attributes are specifically created for testing purposes, decreasing the chances of front-end changes break the tests.
5. Don't do this:
var el = cy.get('selector')
Remember. Cypress is not Selenium! 😅
Cypress has its own architecture, where although in many cases it allows writing code that seems synchronous, even if it is asynchronous (since it puts each command in a queue for later execution), it is not possible to do something like:
const myBtn = cy.contains('button', 'My button') myBtn.click()
However, you can do:
cy.contains('button', 'My button').as('myBtn') cy.get('@myBtn').click()
Most of Cypress's commands are chainable!
6. Do not test external applications
Relying on Google's GUI login for your tests or some third-party API can make your tests unstable, as changes to these services (which you don't control) will break your tests, even if it's all right "on your side."
To ensure that such services are in line with your application, you can have a smoke-test suite, for example, or contract tests.
7. Tests too small or too large
End-to-end tests are not unit tests. Due to the cost of running them, it's worth adding more than one assertion per test to save time.
However, beware of extensive tests. Maybe these are testing a lot of unrelated things and could be broken down into smaller ones.
8. Do not use the
If something goes wrong during the execution of the tests, such hooks run the risk of not being executed, leaving "junk" in the application.
As a good practice, do any cleanup before running the tests, using the
beforeEach hook, for example.
9. Do not use
Cypress already has automatic waiting with different default timeouts to wait for elements to be visible, for animations to end, for requests to be sent and responded and for pages to be loaded.
What you can do to make your tests even more robust is wait for elements to be visible:
Or, you can wait for a particular request that you gave an alias to finish.
cy.intercept(...).as('myReq') ... cy.wait('@myReq')
I wrote about it on
Use mechanisms such as the
start-server-and-test library to initialize the application server before running the tests, ensuring that it will run with the correct code and that the tests will only run when the application is responding.
Besides, such an approach will help you run your tests on a Continuous Integration server.
11. Set the
By setting the
baseUrl in the configuration file (
cypress.json), your tests can visit pages via relative URLs, making them cleaner.
In addition, the
baseUrl can be overwritten via a
cypress.env.json file, environment variables (
CYPRESS_*), command line, plugins, etc., making it possible to run the same tests against different environments.
By following these best practices, you will have fast and robust tests that your team trusts and helps to maintain and evolve.