OVERVIEW
Page Object Model is a Selenium design pattern that can be used with any kind of test automation framework, including keyword-driven, data-driven and hybrid frameworks. One of the problems I faced before using the Page Object Model (POM) design pattern was when one of the elements would change, and I had to update the selectors from all the places where the element was used, especially in large code bases.
So, to change one little selector, I’d have to go all over the code, find out where it’s been used, and change them everywhere, which took a lot of time for such a simple task.
That was before I was using the Page Object Model design pattern. In this blog post on Cypress Page Object Model, I will show you the POM design pattern, why it is important, and how to use Cypress Page Object Model using the Cypress best practices.
Cypress is a test automation framework that is used for testing web applications. It is based on JavaScript and provides a unique set of features like screenshots and records for your test runs that make it well-suited for testing modern web applications. Cypress has been designed from the ground up to be easy to use, reliable, and scalable. Cypress visual regression testing can be used to validate the visual component of a web application.
As such, it has quickly become the framework of choice for many test automation experts. In addition to its core features, Cypress also provides a wide range of plugins and extensions that further enhance its functionality. As a result, Cypress is an incredibly powerful automation testing framework that can be used to test any web application confidently.
Shown below is the download comparison of Cypress vs. Selenium, an indicator that Cypress is gaining a lot of traction:
Akin to Page Object Model in Selenium, POM with Cypress also offers a numerous set of benefits since the overall principles of the design pattern remains the same. Cypress Page Object enables you to reuse code and avoid code duplication, makes your tests more maintainable, and helps to keep your code clean and organized.
In this tutorial on Cypress Page Object Model, we'll look at how to start using the Cypress Page Object Model design pattern and build a robust test automation suite.
The Page Object Model (POM) is a design pattern used in software development where classes represent pages. POM can make code more maintainable and reduce duplication.
All page elements are stored in that class for the related page. This makes it easy to access and update page elements as needed, especially when there are changes in the front end (or UI) of the website.
Page Object Model also makes it easy to reuse code, which can save time and money when testing web applications.
There are several advantages of using the Cypress Page Objects design pattern; some major ones are elbow:
Overall, the Page Object Model is a valuable design pattern that can be used to simplify the Cypress UI testing process. When used in conjunction with Cypress, Page Objects can offer significant benefits in terms of readability, maintainability, and performance.
To run Cypress on your local machine:
$ npm init -y
In order to install Cypress:
$ npm install cypress --save-dev
$ yarn add -D cypress
This command will download and install the latest version of Cypress in your project. As you can see below, the latest version right now is cypress@10.4.0...
"scripts": {
"cypress:run": "cypress run",
"cypress:open": "cypress open"
},
...
$ npm run cypress:open
This will run your Cypress client.The cypress.config.js file is used to edit the Cypress settings. For example, setting the base URL for every request or visit done by Cypress.
The E2E.js file is processed and loaded automatically before each one of your test files. This is a good place to put global configuration and behavior that modifies Cypress.
The commands.js file lets you write your custom commands so you can use or reuse them later in your tests. This file also lets you overwrite the existing commands.
An example of adding a new custom command:
Cypress.Commands.add('login', (email, password) => { ... })
When it comes to Cypress Page Object Model, Fixtures are a great way to mock data for responses to routes. This is especially useful when you need to test something that is not yet implemented or when you want to avoid making real requests. Cypress Fixtures are easy to set up and use, and they can save you a lot of time and effort when writing tests.
Congrats, now our Cypress dashboard is ready and we can write tests and run them.
If we have a look at our folder structure, we can see that Cypress has also automatically created a folder with the name “cypress” which also has some files for the fixtures and custom commands.
Since we are using a Cypress Page Object design pattern, I will create a folder with the name “pages” to store all of the page objects we need in our test.
Let’s create our first object in cypress/pages/Home.js, which will store all of the home page methods.
For this example, we will be using the LambdaTest E-Commerce Playground to run all of the tests on it.
We will test the home page to ensure our application works as expected. We will be using the Cypress Page Object Model design pattern to create methods we can reuse throughout the test suite
So, the first step will be setting this link to our baseUrl property in our cypress.config.js file
const { defineConfig } = require("cypress")
module.exports = defineConfig({
e2e: {
setupNodeEvents(on, config) {
// implement node event listeners here
},
baseUrl: "https://ecommerce-playground.lambdatest.io",
},
})
For simplicity, we will write some methods that will help us to get access to certain elements.
Let’s say you want to write some methods that will give you access to each element in the navbar
Let’s write our first class for the home page.
class Home {
visit() {
cy.visit("/")
}
searchInput(text) {
return cy.get('input[name="search"]').first().type(text)
}
getSearchButton() {
return cy.get("#search > div.search-button > button").first()
}
getHomeButton() {
return cy.get(
"#widget-navbar-217834 > ul > li:nth-child(1) > a > div > span"
)
}
getSpecialButton() {
return cy.get(
"#widget-navbar-217834 > ul > li:nth-child(2) > a > div > span"
)
}
getBlogButton() {
return cy.get(
"#widget-navbar-217834 > ul > li:nth-child(3) > a > div > span"
)
}
getMegaMenuButton() {
return cy.get(
"#widget-navbar-217834 > ul > li.nav-item.dropdown.dropdown-hoverable.mega-menu.position-static > a > div > span"
)
}
getAddOnsButton() {
return cy.get(
"#widget-navbar-217834 > ul > li:nth-child(5) > a > div > span"
)
}
getMyAccountLink() {
return cy.get(
"#widget-navbar-217834 > ul > li:nth-child(6) > a > div > span"
)
}
}
You can get access to these selectors quickly by using the Chrome DevTools, right-click on an element and then open the “copy” dropdown, and then select “copy selector”.
As you can see, some of the selectors might be a bit long, and if we didn’t use the Cypress Page Objects design pattern, we’d have to write all of these in our spec files, which would make the code look messy and we’d have a lot of code duplication. You can learn more about selectors through this locator tutorial.
Let’s create another object for our blog page. I will create a file in the pages folder with the name of the page, which is Blog.js.
// cypress/pages/Blog.js
class Blog {
constructor() {
this.url = "/index.php?route=extension/maza/blog/home"
this.title = "Blog"
}
}
In this object, we will store some methods specific to this page.
Let’s write some methods that will give us access to specific elements.
class Blog {
constructor() {
this.url = "/index.php?route=extension/maza/blog/home"
this.title = "Blog"
}
visit() {
cy.visit(this.url)
}
getFirstCategoryButton() {
return cy.get("#entry_210963 > div > a:nth-child(1)")
}
getSecondCategoryButton() {
return cy.get("#entry_210963 > div > a:nth-child(2)")
}
getThirdCategoryButton() {
return cy.get("#entry_210963 > div > a:nth-child(3)")
}
getPreviousBlogsButton() {
return cy.get(
"#mz-article-listing-76210960 > div.mz-tab-listing-header.d-flex > div > div > a.mz-swiper-nav-prev.swiper-button-disabled"
)
}
getNextBlogsButton() {
return cy.get(
"#mz-article-listing-76210960 > div.mz-tab-listing-header.d-flex > div > div > a.mz-swiper-nav-next"
)
}
}
module.exports = Blog
Now that we have created our page objects, it is time to write some tests.
I will write some tests for the home page in cypress/e2e/home.cy.js, which will include all the tests for the home page.
import Home from "../pages/Home"
const home = new Home()
describe("testing home page", () => {
it("should visit home page", () => {
home.visit()
})
it("should search for a product", () => {
home.searchInput("iphone")
home.getSearchButton().click()
})
})
This code is implementing basic tests, the first one is just visiting the page, which is taken from the Page.js or the Page Object, as you can see, this will be useful because throughout your code, you'll need to re-use this code to visit your home page, and same goes with all the other methods.
The second test gets access to the input element and types some text; after that, it will click the search button.
Now let’s run the test by running the command cypress:open or cypress:run as we have defined the scripts from our package.json file.
I will create a test file similar to the one on the home page with the name blog.cy.js.
The test code:import Blog from "../pages/Blog"
const blog = new Blog()
describe("testing blog page", () => {
beforeEach(() => {
blog.visit()
})
it("should visit the blog page", () => {
cy.title().should("eq", "Blog - Poco theme")
})
it("should have correct category names", () => {
blog.getFirstCategoryButton().should("contain.text", "Business")
blog.getSecondCategoryButton().should("contain.text", "Electronics")
blog.getThirdCategoryButton().should("contain.text", "Technology")
})
})
As you can see, both of the tests are running successfully, and the Cypress Page Objects Model design pattern makes it possible for us to write our code in such a way that we don't have any unnecessary clutter in our code.
This also makes it extremely easy when we have to update our code, let’s say in the future we decide to change some of the elements, and the selectors break and now the tests are failing, if the tests weren’t written using the Cypress Page Objects, we’d have to update the code for our tests in every single place in which the changed elements were present.
With the Cypress Page Object Model design pattern, we only have to update the code for the changed elements only in one place of the code, and that place is the file that has the page object inside of it.
One of the potential shortcomings of local testing with Cypress is that it takes too many resources to the point that you can’t run a lot of parallel tests at once. Well, no machine does not have infinite CPU and RAM :) Hence, you’ll be forced to run only a handful of tests on one browser at a time, which could be very time-consuming if you have a huge codebase.
Cloud testing platforms like LambdaTest help run multiple Cypress E2E tests on multiple browsers with different versions, all in parallel in the cloud.
LambdaTest is a cross browser testing cloud that lets developers use Cypress for their integration testing. It provides an online Cypress grid of 40+ browser versions, on which developers can run their Cypress tests in parallel. This cuts down the testing time from days to hours and helps developers to deliver their projects on time. LambdaTest also provides several features like screenshots, video recordings, and logs, which help developers debug their tests easily.
Subscribe to the LambdaTest YouTube Channel and stay updated with the latest tutorials around Selenium testing, End-to-End (E2E) testing, CI/CD, and more
Also, setting up tests on LambdaTest is very easy, so let’s get started.
Though the latest version of Cypress on LambdaTest is 10.0.0, we would be using version 9.0.0 since Cypress 10 was released on LambdaTest only a few weeks back.
Since we have to change our version to 9.0.0 we also have to use cypress.json for our Cypress configurations. So, let’s create our configurations for Cypress.
{
"baseUrl": "<https://ecommerce-playground.lambdatest.io>",
"integrationFolder": "cypress/e2e" // telling Cypress where to look for test files
}
The lambdatest-cypress-cli is LambdaTest's command-line interface (CLI) aimed to help you run your Cypress tests on the LambdaTest platform.
In order for us to run our Cypress tests on LambdaTest, install the LambdaTest Cypress CLI using the following command:
npm install -g lambdatest-cypress-cliThe lambdatest-cypress-cli provides us with a specific command to create a configuration file, which the LambdaTest platform then recognizes and will execute the tests based on the provided settings inside of the configuration file, which is lambdatest-config.json.
To create the configuration file for LambdaTest, run the following command:
lambdatest-cypress initThis will save the configurations inside the lambdatest-config.json file.
This file stores all of the configurations that are needed to run your Cypress tests on the LambdaTest platform, like telling LambdaTest to run your tests on a specific browser with its version, or running your tests on multiple browsers, etc.
Here is an example of the lambdatest-config.json file.
{
"lambdatest_auth": {
"username": "<Your LambdaTest username>",
"access_key": "<Your LambdaTest access key>"
},
"browsers": [
{
"browser": "Chrome",
"platform": "Windows 10",
"versions": [
"latest-1"
]
},
{
"browser": "Firefox",
"platform": "Windows 10",
"versions": [
"latest-1"
]
}
],
"run_settings": {
"cypress_config_file": "cypress.json",
"build_name": "cypress-pom",
"parallels": 1,
"specs": "./**/*.cy.js",
"ignore_files": "",
"feature_file_suppport": false,
"network": false,
"headless": false,
"reporter_config_file": "",
"npm_dependencies": {
"cypress": "9.0.0"
}
},
"tunnel_settings": {
"tunnel": false,
"tunnel_name": null
}
}
What we need to change from this configuration file first are the username and access_key values.
Use your LambdaTest username for the username field, and get your access key from the LambdaTest Dashboard, by going to your profile settings in the LambdaTest Dashboard.
I have also changed the specs property from ./*.spec.js to ./*.cy.js and the Cypress version to version 9.0.0.
In the cypress_config_file specify the configuration file of your Cypress test project, which in our case is in the root directory.
Change the build name with a name of your choice.
Now that our configuration files are ready, it is time for us to upload our tests to LambdaTest and start running them on the cloud.
To run your tests on the LambdaTest cloud, run the following command:
lambdatest-cypress run
If you go to your builds page in your LamdaTest Dashboard, you can see your tests running on the specific browsers specified in the LambdaTest configuration file.
Overall, using Cypress Page Objects makes for more maintainable and reusable code. This can be extremely beneficial in development, as you won’t have to worry about rewriting or updating your tests every time a change is made to the website.
Dawson is a full-stack developer, freelancer, content creator, and technical writer. He has more than three years of experience in software engineering, and he is passionate about building projects that can help people. You can also follow him on Twitter.
Yes. Cypress uses Page Object Model to create reusable test code.
The Dom method is a function that allows you to create an element programmatically in the browser. The Dom method makes it easier for a tester to write tests in Cypress.
In-Page Object Model (POM) design pattern, page objects are separated from the automation test scripts. This design pattern makes the code reusable, which is why Cypress has implemented POM support.
The Page Object Model (POM) is a design pattern in Selenium that creates an object repository for storing all web elements. This improves code reuse and makes it easier to maintain test cases. In POM, each web page of an application is treated as a class file.
Yes, you can use POM with Cypress. In Cypress, POM can be used to create a layer of abstraction.
Page Object is a class representing a web page and holds the mechanisms of interacting with the elements on that page. The Page Factory pattern creates instances of these web page objects, initializing them with the appropriate content.
Get 100 minutes of automation test minutes FREE!!