Patrón Page Object en Selenium para Pruebas de Automatización Web

Introducción al Patrón Page Object

El patrón Page Object es una técnica de diseño en Selenium que modela cada página web como una clace separada. Esta clase encapsula los elementos de la interfaz, como botones y campos de texto, y las operaciones asociadas, como clics e inserciones de datos. Al estructurar las pruebas de esta manera, se aísla la lógica de localización de elementos del código de las pruebas, lo que facilita el mantenimiento cuando cambian los identificadores o la disposición de los elementos en la página.

Este patrón promueve la separación de responsabilidades: la capa de objetos de página gestiona las interacciones con los elementos, mientras que las pruebas se centran en los datos y escenarios de validación. Para implementar pruebas automatizadas, se puede combinar con frameworks como unittest en Python, que proporciona estructura para organizar y ejecutar casos de prueba.

Implementación con Python y Selenium

Se define una clase base que maneja la inicialización del navegador, la apertura de páginas y métodos genéricos para interactuar con elementos. Luego, se crean clases específicas para cada página, heredando de la base, y finalmente se escriben pruebas con unittest.

1. Clase Base para Páginas

Esta clase proporciona métodos comunes para todas las páginas, como abrir URLs, buscar elementos y manejar ventanas.

# Archivo: pagina_base.py
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

class PaginaBase:
    """Clase base con métodos comunes para pruebas de páginas web."""
    def __init__(self, driver_web, url_principal, titulo_esperado):
        self.driver = driver_web
        self.url = url_principal
        self.titulo = titulo_esperado

    def verificar_titulo(self, titulo):
        """Comprueba si el título actual contiene el texto dado."""
        return titulo in self.driver.title

    def _abrir_pagina(self, enlace, titulo):
        """Abre una URL y valida el título de la página."""
        self.driver.get(enlace)
        self.driver.maximize_window()
        assert self.verificar_titulo(titulo), f"Error al abrir la página: {enlace}"

    def abrir(self):
        """Abre la página configurada en la inicialización."""
        self._abrir_pagina(self.url, self.titulo)

    def localizar_elemento(self, *localizador):
        """Busca un elemento con espera explícita y manejo de errores."""
        try:
            WebDriverWait(self.driver, 10).until(
                EC.visibility_of_element_located(localizador)
            )
            return self.driver.find_element(*localizador)
        except Exception as e:
            print(f"No se encontró el elemento {localizador} en la página: {e}")

    def cambiar_frame(self, localizador):
        """Cambia el foco a un iframe específico."""
        return self.driver.switch_to_frame(localizador)

    def ejecutar_script(self, codigo):
        """Ejecuta código JavaScript en el navegador."""
        self.driver.execute_script(codigo)

    def enviar_texto(self, localizador, texto, limpiar_primero=True, clic_primero=True):
        """Escribe texto en un campo, con opción de limpiar o hacer clic antes."""
        try:
            elemento = self.localizar_elemento(*localizador)
            if clic_primero:
                elemento.click()
            if limpiar_primero:
                elemento.clear()
            elemento.send_keys(texto)
        except Exception as e:
            print(f"Error al enviar texto al elemento {localizador}: {e}")

2. Clase para una Página Específica (Inicio de Sesión)

Esta clase hereda de la base y define los localizadores y acciones para la página de login.

# Archivo: pagina_login.py
from selenium.webdriver.common.by import By
from pagina_base import PaginaBase

class PaginaLogin(PaginaBase):
    """Página de inicio de sesión con localizadores y métodos propios."""
    # Localizadores de elementos
    CAMPO_USUARIO = (By.NAME, 'usuario')
    CAMPO_CONTRASENA = (By.NAME, 'clave')
    BOTON_ENVIAR = (By.ID, 'btn_acceder')
    MENSAJE_ERROR = (By.CSS_SELECTOR, "div.mensaje-err>p")
    ENLACE_MODO_DINAMICO = (By.ID, "enlace_dinamico")
    IDENTIFICADOR_USUARIO = (By.ID, "id_usuario")

    def abrir(self):
        """Abre la página de login."""
        self._abrir_pagina(self.url, self.titulo)

    def ingresar_usuario(self, usuario):
        """Escribe el nombre de usuario en el campo correspondiente."""
        self.enviar_texto(self.CAMPO_USUARIO, usuario, limpiar_primero=False)

    def ingresar_contrasena(self, contrasena):
        """Escribe la contraseña en el campo correspondiente."""
        self.enviar_texto(self.CAMPO_CONTRASENA, contrasena, limpiar_primero=False)

    def pulsar_acceder(self):
        """Hace clic en el botón de envío."""
        self.localizar_elemento(*self.BOTON_ENVIAR).click()

    def obtener_mensaje_error(self):
        """Devuelve el texto del mensaje de error."""
        return self.localizar_elemento(*self.MENSAJE_ERROR).text

    def cambiar_a_modo_dinamico(self):
        """Cambia a modo de autenticación dinámica (si aplica)."""
        self.localizar_elemento(*self.ENLACE_MODO_DINAMICO).click()

    def mostrar_id_usuario(self):
        """Muestra el identificador del usuario tras el login."""
        return self.localizar_elemento(*self.IDENTIFICADOR_USUARIO).text

3. Pruebas Unitarias con unittest

Se crea una clase de prueba que utiliza las páginas definidas para ejecutar escenarios de automatización.

# Archivo: prueba_login.py
import unittest
from pagina_login import PaginaLogin
from selenium import webdriver

class PruebaLoginWeb(unittest.TestCase):
    """Casos de prueba para el inicio de sesión en una aplicación web."""
    def setUp(self):
        """Configura el navegador y datos de prueba."""
        self.driver = webdriver.Firefox()
        self.driver.implicitly_wait(30)
        self.url_aplicacion = "http://ejemplo.com/login"
        self.usuario = "usuario_ejemplo"
        self.contrasena = "contrasena_segura"

    def test_flujo_login(self):
        """Prueba el flujo completo de inicio de sesión."""
        # Instancia la página de login
        pagina_login = PaginaLogin(self.driver, self.url_aplicacion, "Aplicación Web")
        # Abre la página
        pagina_login.abrir()
        # Cambia al frame de login si es necesario
        pagina_login.cambiar_frame('frame_login')
        # Ingresa credenciales y envía
        pagina_login.ingresar_usuario(self.usuario)
        pagina_login.ingresar_contrasena(self.contrasena)
        pagina_login.pulsar_acceder()

    def tearDown(self):
        """Cierra el navegador después de cada prueba."""
        self.driver.quit()

if __name__ == "__main__":
    unittest.main()

Esta estructura permite mantener las pruebas desacopladas de los detalles de implementación de la interfaz. Si los localizadores de elementos cambian, solo se actualiza la clase de página, no los casos de prueba.

Etiquetas: Selenium Python Unittest Page Object pruebas automatizadas

Publicado el 7-14 19:49