Lección 3 de 6 · 3 min
Page Object a fondo (y cuándo se vuelve un monstruo)
Al terminar sabrás
11 min- Implementar Page Object con sus cuatro reglas
- Detectar el God Object y partirlo en component objects
- Saber cuándo el POM no es la mejor opción
Construyes: Un page object sano y su component object extraído, con las reglas aplicadas.
El patrón más usado y peor implementado
Todo el mundo dice que usa Page Object. Casi nadie lo usa bien. El patrón es simple: encapsular los selectores y las acciones de una página en una clase, para que los tests hablen de negocio y un cambio de UI toque un archivo.
Lo difícil no es escribirlo: es mantenerlo sano mientras la suite crece.
Las cuatro reglas del POM sano
1. Ningún selector fuera del page object. Si un test tiene un getByRole o un By., el patrón ya se rompió.
2. Cero assertions dentro del page object. El page object informa, el test juzga:
// ❌ El page object juzga: el test ya no dice qué se espera
class ConfirmacionPage {
async verificarExito() {
expect(await this.mensaje.textContent()).toContain('reservada');
}
}
// ✅ El page object informa, el test juzga
class ConfirmacionPage {
async mensaje() {
return this.page.getByTestId('confirmacion').textContent();
}
}
// en el test:
expect(await confirmacion.mensaje()).toContain('reservada');
La única excepción tolerada: verificar al construir que estás en la página correcta.
3. Los métodos representan acciones del usuario, no clics sueltos. reservar(servicio, hora) es una acción; clickBotonAzul() es un clic disfrazado.
4. Los métodos devuelven page objects, para que la navegación quede encadenable:
const confirmacion = await reservas.reservar('Corte', '15:30'); // devuelve ConfirmacionPage
El God Object: cómo se llega
Nadie decide construir un monstruo. Se llega así: alguien necesita un helper y lo pone en BasePage "porque lo van a usar todos". Al mes hay 20 métodos. Al año, 47 — y nadie sabe cuáles se usan.
La cura: component objects y composición
Un CheckoutPage con 22 métodos no necesita más orden interno: necesita partirse.
// En vez de un CheckoutPage que hace todo:
class CheckoutPage {
constructor(page) {
this.direccion = new DireccionForm(page); // component object
this.pago = new PagoForm(page); // component object
this.resumen = new ResumenCarrito(page); // component object
}
}
// El test queda expresivo:
await checkout.direccion.completar(datos);
await checkout.pago.pagarCon(tarjeta);
expect(await checkout.resumen.total()).toBe('$18.500');
Un component object modela un widget reutilizable (date-picker, modal, tabla, buscador). Si aparece en 5 páginas, se modela una vez y las páginas lo componen.
Composición sobre herencia: heredar de BasePage acopla todo con todo. Componer piezas te deja cambiar una sin tocar el resto. Es la misma lección que en cualquier código.
Cuándo el POM no es lo mejor
El POM asume que la app se organiza en páginas. No siempre es así:
- Apps de una sola pantalla con muchos estados: modelar por componentes o por flujos encaja mejor.
- Suites muy orientadas a negocio: el patrón Screenplay (actores que ejecutan tareas) expresa mejor el "quién hace qué", a costa de más ceremonia.
- API testing: no hay páginas. Ahí modelas clientes de servicio, no page objects.
El POM es la opción por defecto y en el 80% de los casos es la correcta. Solo recuerda que es un medio —separar el qué del cómo—, no un fin. Si te obliga a contorsiones, el patrón no calza con tu app.
Comprueba las reglas en el quiz y parte el monstruo en el reto.
Compruébalo
0/3Responde sin mirar atrás. Verás la explicación al instante.
1. ¿Qué NO debe tener un page object?
2. Tu BasePage tiene 47 métodos utilitarios y todos los page objects heredan de ella. ¿Qué pasa?
3. Un date-picker complejo aparece en 5 páginas distintas. ¿Dónde lo modelas?
Reto: parte el monstruo
Tienes un CheckoutPage con 22 métodos: algunos de la dirección de envío, otros del pago, otros del resumen del carrito, y 4 que hacen assertions.
Propón cómo lo refactorizas.