Cypress condition test

Condition test The following programming paradigms can be clearly expressed:

If X, then Y, else Z

Today, modern JavaScript applications are highly dynamic and variable. Their state and DOM change over time.

The problem with conditional testing is that it can only be used when the state is stable. In modern applications, it is often impossible to know when the state is stable.

For humans - if there is a change of 10 or 100 milliseconds from now on, we may not even notice the change and assume that the state is always the same.

For robots - even 10 milliseconds represents billions of + clock cycles. The difference in time scales is incredible.

People also have intuition. If you click a button and see a load spinner, you assume that the state is changing and automatically wait for it to complete.

A robot has no intuition - it will do it exactly as programmed.

To illustrate this, let's give a simple example to try to conditionally test unstable states.

The DOM is unstable

The following is our application code, which adds the newly created button element to the active CSS class in a random time interval.

// your app code

// random amount of time
const random = Math.random() * 100

// create a <button> element
const btn = document.createElement('button')

// attach it to the body
document.body.appendChild(btn)

setTimeout(() => {
  // add the class active after an indeterminate amount of time
  btn.setAttribute('class', 'active')
}, random)

Look at the following test code:

// your cypress test code
it('does something different based on the class of the button', () => {
  // RERUN THIS TEST OVER AND OVER AGAIN
  // AND IT WILL SOMETIMES BE TRUE, AND
  // SOMETIMES BE FALSE.

  cy.get('button').then(($btn) => {
    if ($btn.hasClass('active')) {
      // do something if it's active
    } else {
      // do something else
    }
  })
})

We will randomly enter the if and else branches.

Server side rendering

If the source code of a web application is rendered on the server side and there is no possibility of subsequently modifying the DOM asynchronously through JavaScript, this application is an excellent alternative for conditional testing.

After the load event, the DOM will not change again, which means that it has reached a stable state of truth

Client side rendering

For modern Web applications, when the load event occurs, nothing is usually rendered on the screen. JavaScript loads the content dynamically and performs rendering.

In this case, it is impossible to rely on DOM for conditional testing, unless we are 100% sure to find a certain time point. When the time point arrives, all asynchronous rendering has ended, and there are no pending network requests, setTimeouts, intervals, postMessage, or async/await code All web developers understand that this is impossible.

Even if we use zone in our application JS, there is no way to capture all asynchronous programming points.

A/B campaign

Use the url parameter to explicitly tell the server what data we expect to return:

// tell your back end server which campaign you want sent
// so you can deterministically know what it is ahead of time
cy.visit('https://app.com?campaign=A')

...

cy.visit('https://app.com?campaign=B')

...

cy.visit('https://app.com?campaign=C')

It is crucial that you understand how your application works else you will write flaky tests.

Look at the following code:

// app code
$('button').on('click', (e) => {
  // do something synchronously randomly
  if (Math.random() < 0.5) {
    // append an input
    $('<input />').appendTo($('body'))
  } else {
    // or append a textarea
    $('<textarea />').appendTo($('body'))
  }
})

The application will randomly insert input or textarea into the body

We can use the following code for conditional testing:

// click the button causing the new
// elements to appear
cy.get('button').click()
cy.get('body')
  .then(($body) => {
    // synchronously query from body
    // to find which element was created
    if ($body.find('input').length) {
      // input was found, do something else here
      return 'input'
    }

    // else assume it was textarea
    return 'textarea'
  })
  .then((selector) => {
    // selector is a string that represents
    // the selector we could use to find it
    cy.get(selector).type(`found the element by selector ${selector}`)
  })

Before the application reaches a stable state, we should take measures to prevent Cypress from further executing commands.

Keywords: Javascript Front-end TypeScript

Added by dsjoes on Sat, 01 Jan 2022 16:36:48 +0200