Cypress Reusability: Custom Commands, BaseUrl and POM
In the previous article of Cypress learning series we have created a test for user registration. During this test we explained the concepts of working with hooks, finding elements and some functions for interacting with them, checking response status and aliasing. In this article we will improve what we did the last time. We will explore the concepts of BaseUrl, Page Object Model (POM) and custom commands in Cypress. All of these provide a higher level of reusability of our test framework.
BaseUrl helps us switch testing environments
In certain cases, we run our tests in different environments like test, staging, pre-production, etc. All of these presumably have different URLs and hardcoding those URLs in tests makes them difficult to maintain. Or let’s imagine that DevOps migrate our test environments to a new infrastructure and parts of the base URL change. We would have to make that change manually in every place where we mention this URL which can be hundreds or even thousands of places. The best option would be to have a single place where we enter the URL of the test environment so in case of changes we need to make the change in just one place.
This is exactly what baseURL property helps us do. Just to remember, we had in our tests the URL for opening the website under test and also we added the same in the intercept functions.
We can take this entire URL https://ecommerce-playground.lambdatest.io and store it as BaseURL. Normally, we would do that in cypress.config.js
file which should look like this now:
What we need to do next is to apply the changes from this setting in our tests. To do that, we simply delete this part of the URL from all references.
If we run the test now with these changes, nothing should change. The test should still pass without any differences. Remember to remove the old URL from every reference in the test. If you need to use it in different places just copy the entire URL of the page you need and then delete the base part.
BaseURL best practice
It is possible to get the baseUrl from Cypress.config
function although it feels redundant and just adds complexity to your tests.
cy.visit(Cypress.config('baseUrl'));
We have already seen that cy.visit
command works with passing just / as an argument. Using baseUrl like above example is too much and does not provide any advantage.
Page Object Model in Cypress
Page Object Model is the most commonly used model in custom frameworks for automated test execution. It is very good for beginners to understand concept of abstraction. Although it has some limitations it still is the most widely used model among Quality Engineers. The main point in POM is to have functions and locators for elements on a single page grouped in a class and then those functions can be reused. We will explore a bit different POM with Javascript classless objects.
As you have noticed in our tests we repeat the selectors in each test because we interact with the same elements. In case of change in for example username selector in HTML we would have to change the selector everywhere manually. If we add these selectors in Javascript object and import that object in our test file we would be able to use the items from that object in our tests. Most importantly in case of any changes in HTML we need to make the change in one place only. Second advantage of such behavior is we can make the selectors more readable and therefore debugging would be a lot easier.
POM implementation
Let’s first create a new folder called Selectors and in it a Javascript file called registrationPageSelectors.js
We will create a Javascript object in it and add all the selectors from registration page.
In the next steps we need to import this file in our userRegistration.cy.js
test file and replace all the raw selectors with object properties. This needs to be added at the top of the file.
import registrationPageSelectors from "../../../selectors/registrationPageSelectors";
Now it looks a lot cleaner and easier to understand what line is for which element. Remember that we need to this also for the elements from the beforeEach
block where we open the hover over the button and click on Registration button. Running the test again should do exactly the same like previously.
Custom commands in Cypress
Cypress provides us with ability to create functions for those actions we often use. We can extract a snippet of code in a custom command and reuse it whenever we need. Custom commands are stored in command.js
file in Support folder which was auto-generated when we installed Cypress.
In this file we have auto-generated commands which are commented out and we can create our commands but also we can override existing Cypress commands. We want to create a command for filling out the form which would take an object with information needed for registration as an argument. In general we can create this command with completing the entire form and clicking on submit but there is a case in our tests which we cannot handle with such custom command. That is the case for checking sending of the form when privacy policy is not accepted. We would have to implement a conditional logic in the command for selecting and deselecting the privacy policy. Such conditional logic is not recommended.
For storing the objects with data we will create a new Javascript file. Add new folder in Cypress folder with name “data” and add new file in it called userRegistrationData.js
. In this file we will add the objects for each of the cases we need to test.
Create new command
Let’s start with creating new command. Open commands.js
file and create new command with name completeRegistrationForm
. As a parameter we expect the object with user data. We can put the parameters for each of the fields but there are too many of them to keep the command readable. Therefore it is better to create an object with properties matching each of the fields needed to populate. Like this we have just one parameter for this command.
Finally, let’s apply this command to our test. We need to apply this command in every it
block where applicable. Here is an example for one of the blocks.
As you can see we replaced the large number of lines in the test with one line command. This command is reusable and we use it with different arguments in different it
blocks. Once again, running the test should not show any different results.
Conclusion
In this article we tackled optimization of our tests by making them more readable, less prone to high maintenance costs and reusable. BaseUrl, Page Object Model and custom commands are important parts of every testing framework. Cypress invested a lot of effort to simplify the usage of their product and improve the overall user experience.