Cypress for Dummies 3

Page Object model.

Mauro Pedano
6 min readNov 13, 2023

I will start with a clarification, this design pattern has been virtually the default architecture pattern for a lot of automation efforts for a loooong time. We all agree on the benefits it provides, but sometimes we neglect some of the complications that it provides in the long term. I think the reason why we appreciate more modern approaches is because we already know Page Object Model (POM for short) and we have implemented it enough times to realize its pains. This is not the vanilla POM (lol), I will try to explain the important parts that we are implementing here, but I think any QA that is starting their path in automation should know about the pattern, I encourage you to research about it and how it is used on different frameworks, like Selenium. Another disclaimer is that page objects is thought to be used with Object Orienting Programming, that’s where it got its name. In Cypress we will be using Functional Programming so we are using some of the concepts for POM but adapted to FP.

I won’t go over the ‘why’, because I want to keep this article short and practical. The benefits of POM are:

  1. Improved Readability and Maintenance: POM separates the page structure and layout from the test scripts, making the code more readable and easier to maintain. Changes in the UI can be made in one place rather than in each test, which simplifies maintenance.
  2. Reusability of Code: Page classes can be reused across multiple test scripts, reducing code duplication. This not only saves time but also reduces the potential for errors.
  3. Reduced Script Modification: Since changes to the UI only require updates to the page objects and not the test cases, there is less need to modify the test scripts when the UI changes, leading to a more stable test suite.
  4. Modularity: The model encourages a modular approach to test script development. By creating modular test cases, testers can build more scalable and understandable test scripts.
  5. Enhanced Collaboration: Since POM results in more organized and understandable code, it’s easier for teams, especially those with multiple automation engineers, to collaborate on test script development.
  6. Easier Troubleshooting: With clear separation of page structure and tests, it becomes easier to identify where issues lie, whether in the test script logic or the page object implementation.
  7. Integration with Other Tools: POM works well with many testing tools and frameworks, making it a versatile approach that can integrate into a variety of testing ecosystems.

Well, having said that, let’s start with today’s topic. Shall we?…

Implementing POM is simple but as anything else when it comes to coding, you need to be consistent. The base idea is that we will divide the automation of features in different layers (separated directories), and also in different files, based on the pages of the AUT (application under test).

The base layers are:

  • Page objects: designed to group together all the elements of a page from our AUT.
  • Step definitions: designed to group together all the functions to interact with a page from our AUT.

optional ones are:

  • Features (if you have implemented some form of gherkin)
  • Utils (if you create some complex logic that can be reused you can use this files)

Step 1: Creating Page files

(these would be clases in a OOP project).

Let’s start with page objects that are the files that define the POM pattern itself. The purpose of this files is to contain the elements you will interact with inside every page of your application. Going back to the example we need 2 pages to implement our test.

I will create those files, but fist, we need a place to store them. The ‘pageobjects’ directory.

  • home.page.js
  • login.page.js

Step 2: Identifying Page Elements

Now that we have the pages, we need to identify what elements we will interact from it.

Here we identify the selectors we need to migrate to their propper page files.

And we create those elements as constants in the propper file. As you will realize, some of the selectors are repeated. In order to be able to reuse them and to have only one place to update if any of them changes, we can create constants for each element in the propper file. I.E. if the ID for the login button selector changes, I have to change it only in the page object and the test will continue working as expected.

Here we can see the elements

I will replace the selectors by their strings now, and you will start to see how the code becomes much more readable.

Step 3: Implementing high order functions.

Now we will create high order functions that we can use to handle some of the interactions with our pages. I will start by creating a base page. In OOP we used this Page Factory pattern to handle common logic across all pages.

I will just use it to store some neat functions that will help me simplify the interaction with the pages, for things like clicking elements and inputing text in fields.

High order functions that can be reused to interact with any page element.

Regarding the implementation of functions we can also implement the basic interaction of the elements on the pages.

Here we can see the helper functions that represent the basic interaction with the elements from the pages.

Step 4: Implementing step definitions.

When we have some steps that have some logic that is more complicated or when we identify that some of the functions we need to perform can be reused or repurposed later with different logic, we can implement a step definitions layer in which we will call the functions from the pages. Here I did a simple example with the login logic to illustrate this practice.

Here we can see how easily we can reuse the logic for the login with different values.
The files structure in the e2e directory so far.
BEFORE
AFTER

Final thoughts

One thing that I would recommend you pay attention in the example are the good naming for elements and for the functions, having functions and variables which names represent parts of the application can help you easily identify the place where something is broken, if the functionality fails and reduce the time it takes you to debug and fix the tests.

Some other practices include naming your pages and steps files based on the same name the team has given the AUT page. IE: login.
we have a login.page.js file and a login.steps.js

Another thing you can consider is moving your tests to a single directory in which you can organize them better. for example a feature folder. (If people request this enough I can create a tutorial on how to implement cucumber and use gherkin for your tests).

Gracias, Vuelva Prontos

--

--

No responses yet