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

VentajaDescripción
Simplifica la escritura de pruebasEl código de prueba utiliza APIs de alto nivel (comologinPage.login('user', 'pass')) en lugar de operaciones Locator primitivas.
Reduce los costos de mantenimientoLos cambios en la estructura de la página solo requieren modificar las clases de Page Object, no cada prueba.
Mejora la legibilidadEl código de prueba se parece más al lenguaje de negocio, incluso los no técnicos pueden entenderlo.
Evita la duplicaciónLos 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

// Ruta del archivo: pages/LoginPage.ts
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

// Ruta del archivo: tests/login.spec.ts
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

// Ruta del archivo: pages/DashboardPage.ts
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

test('Flujo completo de inicio de sesión y cierre de sesión', async ({ page }) => {
  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ácticaDescripción
Localizadores concentrados en el constructorTodos los localizadores se definen en el constructor, no los crees dinámicamente en los métodos.
Los métodos devuelven Page ObjectsLos 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 abusesLas combinaciones comunes de aserciones se encapsulan como métodos; las aserciones simples se escriben directamente en las pruebas.
Usa composición en lugar de herenciaEvita la herencia profunda; usa el patrón de composición para asociar múltiples Page Objects.
Un conjunto de objetos cubre una página/componenteCon granularidad adecuada; no crees una clase separada para cada elemento pequeño.

POM 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.

Otras extensiones