Fixing Intermittent Failures in Cypress: Best Practices for Stable Testing

Vishmi Perera
3 min readAug 30, 2023

Intermittent failures are those elusive test cases that exhibit inconsistent outcomes, passing and failing seemingly at random across different test runs. These flaky tests can be a pain to tackle due to their unpredictable nature. Several factors contribute to their emergence,

  1. Improper selector and assertion usage
  2. Long test specifications
  3. Inadequate handling of network latency.

Improper Selectors and Assertions

Accurate element selection and precise assertions are the backbone of reliable testing. If selectors are not well-defined or assertions are too vague, tests might not consistently target the intended elements or verify expected behavior.

Long Test Specifications

Lengthy test specifications can inadvertently introduce timing issues and dependencies, leading to inconsistencies in test outcomes. These tests become susceptible to variations in the application’s state, which can vary between different test runs.

Network Latency

In modern applications, network requests play a pivotal role. However, network latency can introduce flakiness by causing commands to execute before receiving the expected response. This misalignment can lead to assertions failing unexpectedly.

Timeout Configurations

While Cypress has a default timeout of 4 seconds for each command, developers have the flexibility to modify these timeouts based on their requirements.

cy.get('.nav', { timeout: 10000 }).should('be.visible')

This extends the timeout to 10 seconds for the given command, accommodating potential delays.

Network Requests Handling

Sometimes latency of network requests can cause flakiness. Cypress can execute the next command even if the response of the previous request hasn’t returned yet. cy.intercept() comes in handy in these situations where it prevents the next command from executing until the response comes back.

 cy.intercept('**/user/**').as('apiGet');
cy.get('[data-testid="save-user-button"]').click();
cy.wait('@userGet').then((data) => {
cy.get('#username', { timeout: 30000 }).should('be.visible');

Retries

Cypress has introduced retry commands for test cases where it’s possible to configure number of attempts to retry a test case upon a failure.

retries: {
runMode: 2,
openMode: 0
}

This can be configured globally as well as for specific test cases. As per above example test case will retry additional 2 times once it’s failed.

Smaller Test Specs

It’s always a good practice to write the test cases in the most effective and efficient way rather than adding longer specs to the test suite. Not only will it lead to intermittent failures but also will take time to execute the test suite. In a larger test suite , It’s best to write isolated test cases in which they can be executed individually.

Cypress arbitrary wait

Using cy.wait() is is not recommended as a best practice when writing test cases. There is always better options to use before considering cy.wait() command. Here we manually enforce to wait a few seconds before executing the next command. This might be suitable for smaller applications. However, it’s better to consider other alternatives before using this command.

Intermittent failures can be a frustrating challenge in testing, but armed with the right strategies, they can be effectively managed and minimised.

For further Reference:

https://shipshape.io/blog/cypress-and-flaky-tests/

--

--

Vishmi Perera

Software QA Engineer at WSO2 | ISTQB® CTFL | ❄️𝒃𝒆𝒍𝒍𝒆 â𝒎𝒆❄️