El Page Object Model, comúnmente abreviado como POM, es un patrón de diseño que facilita la organización y mantenimiento del código de pruebas automatizadas. En esencia, POM trata a cada página de un sitio web como un objeto, lo que permite manejar interacciones y elementos de forma escalable y limpia. En lugar de referenciar directamente los elementos de la página en múltiples lugares de tus pruebas, se encapsulan en un solo objeto, permitiendo su reutilización y fácil actualización.
¿Cómo implementar el POM en Cypress?
Implementar el Page Object Model en Cypress es sencillo y aporta claridad a tus pruebas automatizadas. Para comenzar, debes crear un directorio específico para tus objetos de página. Esta estructura no solo mejora la organización, sino que simplifica el mantenimiento a lo largo del tiempo.
Creación y estructuración de tus objetos
Crear un directorio Page Objects: Dentro de Cypress, se crea un directorio llamado PageObjects. Allí se almacenarán todas las clases que representarán las páginas del sitio web.
Definir una clase para cada página: Cada página debe tener su clase correspondiente, por ejemplo: loginPage para una página de inicio de sesión. Aquí se almacenan los identificadores de los elementos relevantes en constantes.
// Ejemplo de clase para la página de login
export class loginPage {
constructor() {
this.userInput = 'userLogin';
this.passwordInput = 'password';
this.loginButton = 'loginButton';
}
}
Métodos para interactuar con los elementos: Dentro de cada clase, se crean métodos para manejar acciones en la página. Por ejemplo, un método visit para navegar a la página y un método login para simular el inicio de sesión.
Exportación e inicialización del objeto de página: Esto permite que la clase sea utilizada en las pruebas.
export const loginPage = new LoginPage();
Creación de pruebas con POM
Escribir pruebas legibles y escalables: Utiliza los métodos definidos en las clases para realizar acciones en las pruebas. Esto no solo facilita la lectura y comprensión del código, sino que también hace que las pruebas sean menos propensas a errores cuando cambian los identificadores de los elementos.
¿Cuáles son los beneficios y desafíos del POM en Cypress?
El uso del Page Object Model en Cypress ofrece varios beneficios:
Mantenibilidad: Centraliza la lógica de localización de elementos, lo que hace que las actualizaciones sean sencillas cuando los identificadores cambian.
Reusabilidad: Los métodos y elementos están definidos únicamente en sus respectivas clases, permitiendo su reuso constante sin duplicidad de código.
Claridad y Simplicidad: Facilita la lectura y entendimiento del flujo de pruebas, siendo más evidente qué acción se está llevando a cabo en cada parte de las pruebas.
Sin embargo, es importante considerar también los desafíos:
Algunos desarrolladores argumentan que Cypress, debido a su naturaleza, no requiere seguir el POM rigurosamente, lo que puede plantear un dilema sobre si siempre utilizar este patrón.
Cypress ofrece alternativas nativas como los "Custom Commands", los cuales permiten simplificar aún más las pruebas sin adherirse estrictamente a POM.
Te animo a explorar ambas opciones y decidir cuál se adapta mejor a tus necesidades y estilo de prueba. Continuar aprendiendo y adquiriendo nuevas habilidades te abrirá muchas puertas en el futuro. ¡Adelante, y buen coding!
Esta solucion la implemente usando en lugar contructor una funcion que directamente obtiene los elementos y al retornar el objeto de cypress podemos seguir haciendo accersiones sin errores
exportclassLoginPage{ elements ={usernameInput:()=> cy.get("#user_login"),passwordInput:()=> cy.get("#user_password"),loginButton:()=> cy.contains("Sign in"),};visit(){ cy.visit("http://zero.webappsecurity.com/login.html");}newLogin(username, password){this.elements.usernameInput().should("not.be.disabled").type(username);this.elements.passwordInput().should("not.be.disabled").type(password);this.elements.loginButton().should("not.be.disabled").click();}}
Quisiera preguntarte, teniendo en cuenta que Cypress fallaría en la función login si no encuentra los elementos en pantalla, una función como validateLoginPage() sería redundante en este caso?
Hola, depende mucho de tu caso de prueba y que quieres validar, tal vez en validateLoginPage() lo que quieres es validar que se muestren los elementos de forma correcta validar el nombre o algun titulo en la pagina
/**
* 📄 Page Object para la página de Login de saucedemo.com
*
* Esta clase encapsula todos los selectores y métodos necesarios
* para interactuar y realizar aserciones en la página de login.
* Es el reemplazo moderno de 'zero.webappsecurity.com'.
*/classLoginPage{constructor(){// 🌎 URL de la página de prácticathis.URL='https://www.saucedemo.com/';// 🎯 Selectores de los elementos de la página.// Usamos selectores de `data-test` que son más robustos.this.selectors={userInput:'[data-test="username"]',// 👤 Campo de usuariopasswordInput:'[data-test="password"]',// 🔑 Campo de contraseñaloginButton:'[data-test="login-button"]',// 👉 Botón de "Login"errorMessage:'[data-test="error"]',// ❌ Mensaje de error};// 🧭 Selectores para validar un login exitoso.// En lugar de pestañas, esta página tiene una lista de productos.this.successPageElements={pageTitle:'.title',inventoryList:'.inventory_list',};}// -------------------------------------------------------------------------// 🚀 MÉTODOS DE ACCIÓN (Realizan tareas en la página)// -------------------------------------------------------------------------/**
* Navega a la página de login.
*/visit(){ cy.visit(this.URL);}/**
* Rellena el formulario de login y lo envía.
* @param{string}username - El nombre de usuario a escribir.
* @param{string}password - La contraseña a escribir.
*/doLogin(username, password){ cy.get(this.selectors.userInput).type(username); cy.get(this.selectors.passwordInput).type(password); cy.get(this.selectors.loginButton).click();}// -------------------------------------------------------------------------// 🔬 MÉTODOS DE ASERCIÓN (Verifican el estado de la página)// -------------------------------------------------------------------------/**
* Verifica que los elementos principales del formulario de login están visibles.
*/validatePageElements(){ cy.get(this.selectors.userInput).should('be.visible'); cy.get(this.selectors.passwordInput).should('be.visible'); cy.get(this.selectors.loginButton).should('be.visible');}/**
* Verifica que el usuario ha entrado a la página de productos,
* lo cual confirma un login exitoso.
*/validateSuccessfulLogin(){// Verificamos que el título de la página sea "Products" cy.get(this.successPageElements.pageTitle).should('have.text','Products');// Verificamos que la lista de productos esté visible cy.get(this.successPageElements.inventoryList).should('be.visible');}/**
* Verifica que el mensaje de error de login se está mostrando.
*/validateErrorDisplayed(){ cy.get(this.selectors.errorMessage).should('be.visible');}}// 📦 Exportamos una instancia de la clase para ser usada en las pruebas.exportconst loginPage =newLoginPage();
Testing
// 📦 Importamos nuestro Page Object actualizado para saucedemo.comimport{ loginPage }from'./pageObjects/ClassLoginPage';// -------------------------------------------------------------------------// 🧪 DATOS DE PRUEBA (Actualizados para saucedemo.com)// -------------------------------------------------------------------------constVALID_USER={username:'standard_user',password:'secret_sauce'};constINVALID_USER={username:'bad_user',password:'bad_password'};// -------------------------------------------------------------------------// 📄 SUITE DE PRUEBAS: Login con Page Object Model en saucedemo.com// -------------------------------------------------------------------------describe('🧪 Pruebas de Login usando Page Object Model',()=>{// ⚙️ Este `beforeEach` se ejecuta antes de cada prueba.// Visita la página y verifica que el formulario está listo.beforeEach(()=>{ loginPage.visit(); loginPage.validatePageElements();});// ▶️ CASO DE PRUEBA 1: Login Fallidoit('❌ Debería mostrar un mensaje de error con credenciales incorrectas',()=>{// ACT - Intentamos hacer login con datos inválidos. cy.log('Intentando hacer login con datos inválidos...'); loginPage.doLogin(INVALID_USER.username,INVALID_USER.password);// ASSERT - Verificamos que el mensaje de error aparece. cy.log('Verificando que el mensaje de error es visible...'); loginPage.validateErrorDisplayed();// Verificación extra: La URL no debería haber cambiado. cy.url().should('eq','https://www.saucedemo.com/');});// ▶️ CASO DE PRUEBA 2: Login Exitosoit('✅ Debería permitir el login de un usuario con credenciales correctas',()=>{// ACT - Hacemos login con el usuario válido. cy.log('Haciendo login con un usuario válido...'); loginPage.doLogin(VALID_USER.username,VALID_USER.password);// ASSERT - Verificamos que hemos entrado a la página de productos. cy.log('Verificando que la página de productos se ha cargado...'); loginPage.validateSuccessfulLogin();// Verificación extra: La URL debería haber cambiado a la página de inventario. cy.url().should('include','/inventory.html');});// ▶️ CASO DE PRUEBA 3: Logoutit('👋 Debería permitir a un usuario desloguearse correctamente',()=>{// ARRANGE - Para desloguearse, primero hay que loguearse. cy.log('Haciendo login primero para poder desloguearse...'); loginPage.doLogin(VALID_USER.username,VALID_USER.password);// ACT - Realizamos la acción de logout (el flujo en esta web es diferente). cy.log('Abriendo el menú y haciendo clic en logout...'); cy.get('#react-burger-menu-btn').click();// 🍔 Clic en el menú "hamburguesa" cy.get('#logout_sidebar_link').click();// 🚪 Clic en el enlace "Logout"// ASSERT - Verificamos que hemos vuelto a la página de login. cy.log('Verificando que hemos vuelto a la pantalla de login...'); loginPage.validatePageElements();});});