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.

Leave a Comment

Your email address will not be published. Required fields are marked *