In recent years, Cypress has emerged and become widely used by many QA professionals and developers for automated testing. Cypress is extensively utilized for various purposes, including E2E (End-to-End), integration tests, and even unit tests. Among these, organizing and managing the codebase in Cypress to optimize the framework is also something we should understand and apply for maximum effectiveness. In this article, we will explore one of the methods to organize and manage the codebase in Cypress that enhances the framework's efficiency when using Cypress.
Page Object Model (POM)
The Page Object Model is a design pattern that helps model web pages or the elements (web elements) on a page into distinct objects. Each object contains functions that interact with the web elements specific to that page.
Advantages of the Page Object Model
Easier Maintenance: POM is beneficial when there are changes in the user interface elements. For instance, if a text box is replaced with a dropdown list on the login page, POM makes it easier to identify which page needs modification. Since each page is defined in a separate file, it simplifies maintenance and reduces errors.
Code Reusability: Each screen is independent. Using POM, you can utilize the test code for one screen and reuse it in another test case. There's no need to rewrite code, saving both time and effort.
Improved Code Readability: The structure and organization in POM are quite clear, making it easy for readers to understand the actions performed on that page. If a change impacts a specific part of the code, it can be made efficiently without affecting other objects.
Implementing Page Object Model with Cypress
Let's assume we want to set up a LoginPage. We would create the Page Object as follows:
We will create a LoginPage.js and define Login page object as follows:
export class LoginPage {
weblocators = {
email: '#input-email',
password: '#input-password',
login: '.btn.btn-login'
}
navigateLoginPage() {
cy.visit('/login'); // Navigate to the login page
}
enterEmail(email) {
cy.get(this.weblocators.email).type(email)
}
enterPassword(password) {
cy.get(this.weblocators.password).type(password)
}
clickLogin() {
cy.get(this.weblocators.login).click()
}
}
In this class, we have three methods:
navigateLoginPage(): Navigates to the login page.
enterEmail(): Enters the email into the email input field.
enterPassword(): Enters the password into the password input field.
clickLogin(): Submits the login form by clicking the login button.
After defining the login object, you can use it in test cases as follows:
import { LoginPage } from "../../pages/LoginPage"
const loginPage = new LoginPage()
describe(' test automation', () => {
it(‘login flow', () => {
loginPage.navigateLoginPage()
loginPage.enterEmail('test@example.com')
loginPage.enterPassword('password'')
loginPage.clickLogin()
})
})
Alternatives to the Page Object Model?
However, POM still has its drawbacks. Building and setting up POM can require time and effort, especially for applications with many pages or complex structures. Additionally, page objects can slow down tests because they force the tests to always go through the application's user interface. Maintenance can also be challenging if developing without a strong understanding of how to build a proper POM framework beforehand.
App Actions are an alternative to POM when you want to simplify the framework by focusing on frequently used actions and avoiding the complexity of building a full POM structure.
With App Actions, you only need to write common actions—typically functions used to interact with the application, such as login, search, or register. These actions can then be called in any test, helping to reduce the time needed to build the framework.
Implementing App Actions with Cypress
You can add a login action to the commands.js file (located in the cypress/support/ directory). This allows you to reuse the login action across multiple test cases.
Here’s how you can set up the login action:
Cypress.Commands.add('login', (email, password) => {
cy.visit('/login')
cy.get('#input-email"]').type(email)
cy.get('#input-password').type(password)
cy.get('.btn.btn-login]').click()
});
After defining the action, you can easily use it in any test case as follows:
describe(' test automation', () => {
it(‘login flow', () => {
cy.login('test@example.com', 'password')
})
})
However, if the UI changes frequently, maintaining App Actions can become challenging because the actions are often abbreviated and lack of separation between the UI and logic. Besides, the source code of test cases and actions gets intertwined with the UI, making it difficult to manage significant UI changes.
Conclusion
Using the Page Object Model (POM) for Cypress is a good approach, especially for large projects that require long-term maintenance and development. However, for smaller and less complex projects, using App Actions provides a more optimal solution helping save significant time and avoiding unnecessary complexity.
Disclaimer: The statements and opinions expressed in this article are those of the author(s) and do not necessarily reflect the positions of Thoughtworks.