Playwright Modelo de Objeto de Página (POM)
Este capítulo presenta el diseño y la implementación del modelo de objeto de página (Page Object Model) para ayudarte a organizar grandes suites de pruebas.
¿Qué es POM?
El modelo de objeto de página es un patrón de diseño de pruebas que encapsula los elementos y operaciones de una páginaen clases independientes.en.
Cada objeto de página representa una página o componente de la aplicación web, y contiene los localizadores y métodos de operación de esa página.
Ventajas de POM
| Ventaja | Descripción |
|---|---|
| Simplifica la escritura de pruebas | El código de prueba utiliza APIs de alto nivel (comologinPage.login('user', 'pass')) en lugar de operaciones Locator primitivas. |
| Reduce los costos de mantenimiento | Los cambios en la estructura de la página solo requieren modificar las clases de Page Object, no cada prueba. |
| Mejora la legibilidad | El código de prueba se parece más al lenguaje de negocio, incluso los no técnicos pueden entenderlo. |
| Evita la duplicación | Los mismos localizadores y operaciones solo necesitan definirse una vez. |
Crear clases de Page Object
A continuación, tomando como ejemplo el sitio web EJEMPLO, creamos un Page Object para la página de inicio de sesión.
Ejemplo
import { type Page, type Locator, expect } from '@playwright/test';
export class LoginPage {
readonly page: Page;
// Declara todos los localizadores como propiedades de solo lectura
readonly usernameInput: Locator;
readonly passwordInput: Locator;
readonly loginButton: Locator;
readonly errorMessage: Locator;
constructor(page: Page) {
this.page = page;
// Inicializa todos los localizadores en el constructor
this.usernameInput = page.getByLabel('Nombre de usuario');
this.passwordInput = page.getByLabel('Contraseña');
this.loginButton = page.getByRole('button', { name: 'Iniciar sesión' });
this.errorMessage = page.getByTestId('login-error');
}
// Navega a la página de inicio de sesión
async goto() {
await this.page.goto('/login');
}
// Ejecuta la operación de inicio de sesión
async login(username: string, password: string) {
await this.usernameInput.fill(username);
await this.passwordInput.fill(password);
await this.loginButton.click();
}
// Verifica el mensaje de error al fallar el inicio de sesión
async expectLoginError(message: string) {
await expect(this.errorMessage).toBeVisible();
await expect(this.errorMessage).toContainText(message);
}
}
Usar Page Object en pruebas
Ejemplo
import { test, expect } from '@playwright/test';
import { LoginPage } from '../pages/LoginPage';
test.describe('Función de inicio de sesión', () => {
test('Inicio de sesión exitoso con nombre de usuario y contraseña correctos', async ({ page }) => {
const loginPage = new LoginPage(page);
// Usa los métodos del Page Object
await loginPage.goto();
await loginPage.login('ejemplo_user', 'correct_password');
// Verifica la redirección después de iniciar sesión correctamente
await expect(page).toHaveURL('/dashboard');
});
test('Fallo de inicio de sesión con contraseña incorrecta', async ({ page }) => {
const loginPage = new LoginPage(page);
await loginPage.goto();
await loginPage.login('ejemplo_user', 'wrong_password');
// Usa el método de aserción del Page Object
await loginPage.expectLoginError('Nombre de usuario o contraseña incorrectos');
});
});
Objetos de página de múltiples capas
En proyectos reales, se pueden crear múltiples Page Objects que pueden relacionarse entre sí.
Ejemplo
import { type Page, type Locator, expect } from '@playwright/test';
export class DashboardPage {
readonly page: Page;
readonly welcomeText: Locator;
readonly logoutButton: Locator;
constructor(page: Page) {
this.page = page;
this.welcomeText = page.getByText('Bienvenido de nuevo');
this.logoutButton = page.getByRole('button', { name: 'Cerrar sesión' });
}
async expectWelcomeMessage(username: string) {
await expect(this.welcomeText).toContainText(username);
}
async logout() {
await this.logoutButton.click();
}
}
Uso combinado de objetos de página:
Ejemplo
const loginPage = new LoginPage(page);
const dashboardPage = new DashboardPage(page);
// Iniciar sesión
await loginPage.goto();
await loginPage.login('ejemplo_user', 'password');
// Verifica el dashboard
await dashboardPage.expectWelcomeMessage('ejemplo_user');
// Cerrar sesión
await dashboardPage.logout();
await expect(page).toHaveURL('/login');
});
Mejores prácticas de POM
| Práctica | Descripción |
|---|---|
| Localizadores concentrados en el constructor | Todos los localizadores se definen en el constructor, no los crees dinámicamente en los métodos. |
| Los métodos devuelven Page Objects | Los métodos de operación pueden devolver un nuevo Page Object (por ejemplo, devolver DashboardPage después de iniciar sesión). |
| Encapsula las aserciones, pero no abuses | Las combinaciones comunes de aserciones se encapsulan como métodos; las aserciones simples se escriben directamente en las pruebas. |
| Usa composición en lugar de herencia | Evita la herencia profunda; usa el patrón de composición para asociar múltiples Page Objects. |
| Un conjunto de objetos cubre una página/componente | Con granularidad adecuada; no crees una clase separada para cada elemento pequeño. |
Otras extensionesPOM no es un requisito obligatorio.
Para proyectos pequeños con solo unas pocas pruebas, escribir localizadores directamente en las pruebas es más claro. Cuando haya más de 5 archivos de prueba, considera introducir POM.