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.