Cypress Page Object Model
The Page Object Model organizes test code by grouping selectors and actions for each page into a separate file. This pattern keeps test files clean and makes maintenance easier as a project grows. This topic explains how to build and use this pattern.
The Problem Without Page Objects
Without organization, every test file repeats the same selectors for common pages like login or checkout. A single selector change then requires updating many separate test files. This repetition creates a heavy maintenance burden over time.
A Simple Way to Picture It
Think of a page object as an instruction manual for operating a specific machine. Instead of every worker memorizing button locations from scratch, they consult the same shared manual. Updating the manual once updates the knowledge for every worker using that machine.
Creating a Page Object File
A page object file typically lives inside a dedicated folder, such as cypress/pages.
class LoginPage {
visit() {
cy.visit('/login')
}
fillUsername(username) {
cy.get('#username').type(username)
}
fillPassword(password) {
cy.get('#password').type(password)
}
submit() {
cy.get('#login-button').click()
}
}
export default new LoginPage()Using a Page Object in a Test
import LoginPage from '../pages/LoginPage'
describe('Login', () => {
it('logs in successfully', () => {
LoginPage.visit()
LoginPage.fillUsername('john_doe')
LoginPage.fillPassword('secret123')
LoginPage.submit()
cy.contains('Welcome').should('be.visible')
})
})The test file now reads like a list of clear actions instead of raw selectors and commands.
Page Object Structure Diagram
Test File | v Imports LoginPage | v Calls methods like visit(), fillUsername(), submit() | v LoginPage internally uses cy.get and cy.visit
Adding a Method for Full Login
A page object can combine multiple steps into a single convenient method.
class LoginPage {
login(username, password) {
this.visit()
this.fillUsername(username)
this.fillPassword(password)
this.submit()
}
}LoginPage.login('john_doe', 'secret123')Benefits of the Page Object Model
Updating a selector only requires a change inside the page object file, not every test using it. New team members read test files more easily, since method names describe actions in plain language. This pattern scales well as a project grows to include dozens of pages and hundreds of tests.
Combining Page Objects with Custom Commands
Some teams blend page objects with custom commands, using commands for simple reusable actions and page objects for more complex page-specific logic. This combination gives flexibility depending on the situation.
Organizing Multiple Page Objects
cypress/pages/ LoginPage.js CheckoutPage.js DashboardPage.js
Each file represents one page or one major section of the application, keeping related logic grouped together.
When Page Objects Might Be Overkill
Small projects with only a handful of tests may not need this level of organization yet. Adding page objects too early can add unnecessary structure for a simple project. Introducing this pattern once a project grows past a small number of test files often makes more sense.
Key Points
- The Page Object Model groups selectors and actions for each page into one file.
- Test files become more readable by calling clear, named methods.
- Selector changes only require updates inside the relevant page object file.
- Page objects can combine with custom commands for added flexibility.
- Small projects may not need this pattern until they grow larger.
