Skip to main content
Test Automation Lab
HomeTestingPlaygroundsGuidesToolsRecipesBlog
© 2026 Lucas Câmara de Souza
GitHubLinkedIn
← All recipes

🧱 Page Object Model

Tame brittle E2E suites by hiding selectors and flows behind a small API.

  • e2e
  • maintainability
  • design

Problem

E2E tests are littered with raw selectors and step-by-step interactions. A single UI change forces edits across dozens of tests, and intent is buried under mechanics.

Solution

Wrap each page (or meaningful component) in a class that exposes intent-revealing methods — login(user), addToCart(item) — and keeps selectors private. Tests read like user stories; selector churn is contained in one place.

Keep page objects thin: they model interaction, not assertions. Let the test decide what to assert so the object stays reusable.

Snippets

login.page.ts — selectors and flows live here
export class LoginPage {
  constructor(private readonly page: Page) {}

  // Selectors are private; intent is public.
  private readonly email = () => this.page.getByLabel('Email');
  private readonly password = () => this.page.getByLabel('Password');
  private readonly submit = () => this.page.getByRole('button', { name: 'Sign in' });

  async goto() {
    await this.page.goto('/login');
  }

  async login(email: string, pass: string) {
    await this.email().fill(email);
    await this.password().fill(pass);
    await this.submit().click();
  }
}
The test reads like a user story
test('user can sign in', async ({ page }) => {
  const login = new LoginPage(page);
  await login.goto();
  await login.login('user@example.com', 'secret');
  await expect(page.getByRole('heading', { name: 'Dashboard' })).toBeVisible();
});

Pitfalls

  • Putting assertions inside the page object — it couples the model to one test and hurts reuse.
  • God objects: one giant page class for the whole app. Split by page or component.
  • Leaking selectors back into tests — once they escape, the abstraction stops paying off.

See also

Guide: Designing Reliable E2E Tests →Tool: Playwright →