Best Practices of Cypress/Test Automation framework
In this article, let’s see the best practices of automation in Cypress while creating a framework, few of the practices are applicable to any tool irrespective of the language. Ex: Selenium Webdriver, WebdriverIO, Puppeteer, etc.,
Please check the list of best practices below:
1. Unwanted waits.
2. Chaining Assertions.
3. Global baseurl setup.
4. Environment URL config.
5. Page Object Model.
6. Reusable functions.
7. Constants. etc.,
1. Unwanted waits:
For any framework/tool, adding waits, browser.pause
or Thread.sleep
is an unwanted wait and not a good practice. These waits make the execution of the tests slow and redundant. Cypress automatically waits for all commands and assertions to pass/fail before moving to the next step. No more async hell. But, for complex scenarios, while handling date-pickers or jquery dropdowns, etc., we can use below timeouts.
A. Cypress:
Explicit Timeout — Time, in milliseconds, to wait for a specific element to load in the DOM.
cy.get(loc).type('name', {timeout: 3000};cy.get(loc).type('name', {delay: 100};
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" : 40000
}
ResponseTimeout — Time, in milliseconds, to wait for a response in commands such as cy.request(), cy.fixture(), cy.getCokkies(), etc.,
{
"responseTimeout" : 20000
}
B. Selenium-Webdriver:
Implicit wait -
driver.manage().timeouts().implicitlyWait(TimeOut, TimeUnit.SECONDS);
Explicit wait -
WebDriverWait wait = new WebDriverWait(driver, Timeout);wait.until(ExpectedConditions.visibilityOfElementLoacated(By.id("loc")));wait.until(ExpectedConditions.presenceOfElementLocated(By.id("loc")));wait.until(ExpectedConditions.elementToBeClickable(By.id("loc")));wait.until(ExpectedConditions.visibilityOf(By.id("loc")));
C. WebdriverIO:
Explicit wait -
const ele = $('#el');ele.waitForVisible();
ele.waitForVisible(5000);
PageLoad Timeout -
browser.setTiemout({'pageLoad' : 10000})
Config.js -
// wdio.conf.js
exports.config = {
// ...
waitforTimeout: 5000,
// ...}
2. Chaining Assertions:
Its’ always better to add multiple assertions to a single command instead of multiple lines of code. Cypress runs a progression of async lifecycle occasions that reset state between tests. Resetting tests is much slower than including more statements. Ex:
cy.get(loc)
.should('be.visible')
.and('be.enabled')
.and('have.class', 'active');
3. Global baseurl setup:
Baseurl should be set up in cypress.json
file instead of passing the URL to commands like cy.visit(“https://www.amazon.com/”) and cy.request(“https://www.amazon.com/”).
{
"baseurl" : "https://www.amazon.com"
}
4. Environment config URL:
When executing scripts on multiple environments such as test, staging, and production it’s always better to create an env config file and store the env URL in staging.json
file and call the baseurl from config. So many times we face a situation while executing tests to change the baseurl from ‘test > staging’ or ‘vice-versa’ which is a redundant task. To overcome this issue env config file is the better approach.
Note: Environment config URL is for command-line execution-only using npm scripts.
Steps:
a. Create a config folder and add test.json
staging.json
& productions.json
files.
b. Add baseurl to the config files.
Please check the sample screenshot below.
c. Add below code snippet to plugins/index.js
file
d. Add npm script to package.json
file
"test:staging": "cypress run -r mocha-allure-reporter -e configFile=staging -s "cypress/filename"
e. Execute the scripts by calling the npm script command
npm run test:staging
or
npm run test:qa
5. Page Object Model:
One of the well-known automation test design patterns is the page object model (POM). POM is a design pattern that helps to enhance test maintenance and reduce code duplication. A few benefits of the POM pattern are given below.
* POM helps in diminishing the level of code duplication.
* POM encourages better upkeep of test information, locators and test capacities.
* POM helps in less upkeep of test structure where we store locators and capacities independently from test contents for the simplicity of progress the executives in code.
* Implementation of articles and capacities are isolated from one another, builds the comprehensibility of the code. There are two ways of creating page objects in Cypress.
Ex 1: Storing objects and functions in the same file
>> Test Script file:
import { signIn } from '../../signIn'it('check SignIn', () => {
cy.visit('/')
signIn();
})
Ex 2: Storing objects in one file and calling them in test scripts
>>> Test Script file:
import SignInPage from '../../'const signIn = newSignInPage();signIn
.fillEmail(email);
.fillPass(pass);
.Submit();
6. 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”…
Ex: Reusable file
Test Script file:
import helper from '../helper.js'helper.checkTitle(title);helper.checkTextExist(text);helper.click();
7. Constants:
For any website, while automating we face a situation to assert the 100’s of text displaying on the page. It’s easy to get the text and assert it in the test script file. But in the long run if the text changes on the page then we need to keep update the scripts every time which is a high maintenance task. ex:
cy.title().should('include', 'Get the credit card that puts money back in your wallet');
To simplify the above method, we can create a constants folder and add text.js
to store all strings/text from the page and call them in scripts. If text changes in the future then we can modify it in a single file instead of all files.
export const text_ssTitle = 'Get the credit card that puts money back in your wallet';export const text_ssSubTitle = ' What are you looking for?';export const text_thankyouMessage = 'Thanks for applying a credit card with us!';
Summary:
Finally, the above practices are a better approach to breaking down your framework and organizing the tests to simplify the readability of scripts and less maintenance of the automation. Feel free to comment/add any other best practices for the ease of the test automation.
Thanks!
Happy Scripting!