Page Object Model w Cypressie – testy, które nie bolą

Jeśli robisz testy E2E w Cypressie i masz więcej niż jeden plik z testami, to prędzej czy później dojdziesz do momentu, w którym zaczyna robić się… syf.
Ten sam selektor kopiowany 5 razy. Jak zmienią klasę w HTML, to poprawiasz to w 7 miejscach.
Brzmi znajomo? No właśnie.

I tu wchodzi na scenę Page Object Model (POM) – czyli podejście, dzięki któremu możesz:

  • oddzielić logikę testów od tego, jak pobierasz elementy z DOM-u,

  • łatwiej ogarniać zmiany na stronie,

  • pisać testy, które są czytelne i da się je utrzymać bez płaczu.

I spokojnie – zaraz pokażę Ci, jak to ugryźć krok po kroku.

POM w cypress

Co to jest ten cały Page Object Model?

W skrócie: tworzysz osobne pliki (klasy / moduły), w których trzymasz selektory i akcje na danej stronie. A testy tylko korzystają z tych metod.

To trochę tak, jakbyś sobie zrobił „zestaw zdalnego sterowania” do Twojej aplikacji, żeby w teście nie bawić się w cy.get('.btn.btn-primary'), tylko napisać sobie loginPage.clickLoginButton() i już.

 

Jak wygląda taki POM w Cypressie?

Załóżmy, że testujesz formularz logowania. Zrobisz sobie folder page-objects i w nim plik LoginPage.js.

// cypress/page-objects/LoginPage.js
class LoginPage {
  visit() {
    cy.visit('/login');
  }

  fillEmail(email) {
    cy.get('#email').type(email);
  }

  fillPassword(password) {
    cy.get('#password').type(password);
  }

  clickLoginButton() {
    cy.get('button[type="submit"]').click();
  }

  getErrorMessage() {
    return cy.get('.error-message');
  }
}

export default new LoginPage();

I teraz test wygląda tak:

 

// cypress/e2e/loginTest.cy.js
import loginPage from '../page-objects/LoginPage';

describe('Logowanie', () => {
  it('powinno się nie udać przy złych danych', () => {
    loginPage.visit();
    loginPage.fillEmail('wrong@email.com');
    loginPage.fillPassword('wrongpass');
    loginPage.clickLoginButton();
    loginPage.getErrorMessage().should('be.visible');
  });
});

Czytelne? Mega.
A jak zmieni się #email na .email-input, to zmieniasz to w jednym miejscu, a nie w pięciu testach.

 

Gdzie trzymać selektory? W metodach czy jako osobne właściwości?

Ja robię tak, że wszystko chowam w metodach, bo nie lubię sytuacji, w których masz 10 getterów i potem jeszcze musisz się zastanawiać, czy gdzieś czegoś nie nadpisujesz.

Ale jeśli wolisz podejście typu:

get emailField() {
  return cy.get('#email');
}

to też OK. Ważne, żebyś nie pisał selektorów bezpośrednio w testach – bo wtedy cały sens POM się rozjeżdża.

 

Struktura projektu – jak to rozkminić?

To najprostszy podział, który działa:

cypress/
├── e2e/
│   └── loginTest.cy.js
├── page-objects/
│   └── LoginPage.js

Ale jak projekt rośnie, możesz zrobić folder na każdą większą funkcjonalność:

 

page-objects/
├── auth/
│   └── LoginPage.js
├── dashboard/
│   └── UserProfilePage.js

Nie komplikuj na siłę. Jak masz 3 strony – zrób 3 pliki. Jak masz 20 – podziel sobie folderami.

 

Kiedy nie używać POM?

Page Object Model ma sens… gdy jest co modelować.

Jak masz 3 testy na landing page’u i klikają jeden przycisk – to serio nie ma sensu budować całej struktury, klas, metod i kombinacji.
Ale jeśli testujesz rejestrację, logowanie, koszyk, zamówienia, dashboard itd. – POM to Twój przyjaciel.

 

TL;DR – czyli jak to ugryźć od razu

  1. Robisz plik LoginPage.js

  2. Wrzucasz tam metody typu fillEmail, clickLoginButton

  3. W teście: loginPage.visit() zamiast cy.visit('/login')

  4. Jak coś się zmieni – aktualizujesz tylko jeden plik

  5. Twoje testy są krótkie, czyste i nie chce Ci się płakać przy refaktorze

Na koniec – moja rada

Nie daj sobie wmówić, że POM to „overengineering”. To po prostu dobry nawyk, który ratuje Ci tyłek, gdy testów robi się więcej niż 5.

Nie musisz od razu refaktorować całego projektu – ale spróbuj w nowym teście.
Zrób sobie jedną klasę, podziel akcje, zobacz jak się z tym pracuje.

W razie czego – mam cały kurs z Cypressa (i w nim dokładnie pokazuję, jak to wygląda w praktyce 😉)

Dostępne szkolenia

pakiet premium