Toda suíte E2E começa pequena e organizada. Com o tempo, sem disciplina, ela vira uma teia de seletores duplicados em que mudar um botão quebra trinta testes. O Page Object Model (POM) é o padrão que evita esse caos.
A ideia central
Cada página (ou componente) da aplicação vira uma classe que encapsula como interagir com ela. O teste descreve o que fazer; o Page Object sabe onde estão os elementos.
class LoginPage {
constructor(page) { this.page = page; }
async open() { await this.page.goto('/login'); }
async login(u, p) {
await this.page.getByLabel('E-mail').fill(u);
await this.page.getByLabel('Senha').fill(p);
await this.page.getByRole('button', { name: 'Entrar' }).click();
}
}
O teste fica limpo e fala a linguagem do negócio:
const login = new LoginPage(page);
await login.open();
await login.login('qa@exemplo.com', 's3nh4');
await expect(page.getByText('Bem-vindo')).toBeVisible();
Regras que mantêm o POM saudável
- Sem asserts dentro do Page Object — ele age sobre a página; quem valida é o teste.
- Um seletor, um lugar — mudou o HTML? Ajusta em uma classe só.
- Métodos por intenção (
login()), não por mecânica (clicarBotao()). - Componentes reutilizáveis (menu, modal) viram objetos próprios, compostos nas páginas.
Quando um redesign quebra a suíte inteira em poucos arquivos — e não em todos — o POM está cumprindo seu papel.
Conclusão
POM não é burocracia: é o que faz a automação escalar junto com o produto. Combinado a localizadores acessíveis e fixtures, mantém a suíte legível e barata de manter mesmo depois de centenas de testes.
Every E2E suite starts out small and organized. Over time, without discipline, it becomes a web of duplicated selectors where changing one button breaks thirty tests. The Page Object Model (POM) is the pattern that prevents that chaos.
The core idea
Each page (or component) of the application becomes a class that encapsulates how to interact with it. The test describes what to do; the Page Object knows where the elements are.
class LoginPage {
constructor(page) { this.page = page; }
async open() { await this.page.goto('/login'); }
async login(u, p) {
await this.page.getByLabel('Email').fill(u);
await this.page.getByLabel('Password').fill(p);
await this.page.getByRole('button', { name: 'Sign in' }).click();
}
}
The test stays clean and speaks the language of the business:
const login = new LoginPage(page);
await login.open();
await login.login('qa@example.com', 's3cr3t');
await expect(page.getByText('Welcome')).toBeVisible();
Rules that keep the POM healthy
- No asserts inside the Page Object — it acts on the page; the test does the validating.
- One selector, one place — did the HTML change? Fix it in a single class.
- Methods by intent (
login()), not by mechanics (clickButton()). - Reusable components (menu, modal) become their own objects, composed into pages.
When a redesign breaks the whole suite in a few files rather than in all of them, the POM is doing its job.
Conclusion
POM isn't bureaucracy: it's what makes automation scale along with the product. Combined with accessible locators and fixtures, it keeps the suite readable and cheap to maintain even after hundreds of tests.