Guía técnica del framework unittest para automatización en Python

El framework unittest es una herrmaienta robusta integrada en la biblioteca estándar de Python, diseñada originalmente para pruebas unitarias. Sin embargo, su versatilidad permite extender su uso a la automatización de pruebas web, proporcionando mecanismos para organizar casos de prueba, realizar aserciones detalaldas y generar reportes de resultados.

Componentes fundamentales de unittest

Para dominar este framework, es esencial comprender sus clases y métodos principales:

  • unittest.TestCase: Es la clase base de la que deben heredar todos los casos de prueba. Proporciona la estructura necesaria para ejecutar métodos de prueba individuales.
  • unittest.main(): Una interfaz de línea de comandos que busca automáticamente métodos que comiencen con el prefijo test y los ejecuta. El orden de ejecución se basa en el orden lexicográfico de los nombres de los métodos.
  • unittest.TestSuite: Permite agrupar múltiples casos de prueba en una "suite" para ejecutarlos de forma conjunta.
  • unittest.TextTestRunner: El motor encargado de orquestar la ejecución de las pruebas y mostrar los resultados en la salida estándar.
  • unittest.defaultTestLoader: Utilizado para cargar pruebas desde módulos o directorios. El método discover() es especialmente útil para encontrar archivos de prueba que sigan un patrón específico (ej. test_*.py).

Decoradores para el control de ejecución

Existen situaciones donde es necesario omitir ciertas pruebas basándose en condiciones específicas:

  • @unittest.skip(motivo): Omite la prueba incondicionalmente.
  • @unittest.skipIf(condicion, motivo): Omite la prueba si la condición se evalúa como verdadera.
  • @unittest.skipUnless(condicion, motivo): Omite la prueba a menos que la condición sea verdadera.
  • @unittest.expectedFailure: Marca la prueba como un fallo esperado.

Ciclo de vida y Aserciones

Cada clase que hereda de TestCase puede implementar métodos para gestionar el estado antes y después de cada prueba:

  • setUp(): Se ejecuta antes de cada método de prueba. Ideal para inicializar controladores de bases de datos o abrir sesiones de navegador.
  • tearDown(): Se ejecuta después de cada método de prueba, sin importar si falló o tuvo éxito. Se utiliza para limpieza de datos o cerrar procesos.

Las aserciones permiten validar los resultados esperados frente a los obtenidos:

self.assertEqual(a, b)      # Verifica si a == b
self.assertIn(a, b)         # Verifica si a está contenido en b
self.assertTrue(x)          # Verifica si x es verdadero
self.assertIsNone(x)        # Verifica si x es None
self.assertIsInstance(a, b) # Verifica la instancia del objeto

Implementación práctica: Lógica y estructura

A continuación, se presenta un ejemplo de cómo estructurar una clase de prueba con diferentes escenarios y métodos de ejecución:

import unittest

class ValidadorMatematico(unittest.TestCase):

    def setUp(self):
        # Preparación del entorno de prueba
        self.valor_referencia = 50

    def test_verificar_igualdad(self):
        resultado = 25 * 2
        self.assertEqual(resultado, self.valor_referencia, msg="La multiplicación no coincide")

    def test_verificar_rango(self):
        valor_test = 60
        self.assertGreater(valor_test, self.valor_referencia)

    @unittest.skip("Demostración de omisión")
    def test_operacion_omitida(self):
        self.assertEqual(1, 2)

    def tearDown(self):
        # Limpieza de recursos
        pass

if __name__ == '__main__':
    # Opción 1: Ejecución automática
    # unittest.main()

    # Opción 2: Suite personalizada
    suite_pruebas = unittest.TestSuite()
    suite_pruebas.addTest(ValidadorMatematico('test_verificar_igualdad'))
    
    ejecutor = unittest.TextTestRunner(verbosity=2)
    ejecutor.run(suite_pruebas)

Integración con Selenium para Pruebas Web

El uso de unittest junto con Selenium WebDriver permite crear scripts de automatización estructurados. A continuación, se muestra una implementación para validar un flujo básico de búsqueda:

from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
import unittest
import time

class SuiteNavegacion(unittest.TestCase):

    def setUp(self):
        self.driver = webdriver.Chrome()
        self.driver.maximize_window()
        self.driver.implicitly_wait(15)
        self.url_destino = "https://www.google.com"

    def test_busqueda_termino(self):
        navegador = self.driver
        navegador.get(self.url_destino)
        
        # Localización de elementos y acción
        cuadro_busqueda = navegador.find_element(By.NAME, "q")
        cuadro_busqueda.send_keys("Python unittest")
        cuadro_busqueda.send_keys(Keys.RETURN)
        
        time.sleep(2)
        
        # Validación del título de la página
        self.assertIn("unittest", navegador.title)

    def tearDown(self):
        if self.driver:
            self.driver.quit()

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

Organización Dinámica de Pruebas

En proyectos de gran escala, es ineficiente añadir manualmente cada caso a una suite. La mejor práctica es utilizar el descubrimiento automático de módulos:

import unittest

def ejecutar_todas_las_pruebas():
    # Define la ruta donde se encuentran los archivos .py de prueba
    directorio_pruebas = './pruebas_modulos'
    
    # Descubre y carga todas las pruebas que coincidan con el patrón
    loader = unittest.defaultTestLoader
    suite_descubierta = loader.discover(start_dir=directorio_pruebas, pattern='test_*.py')
    
    # Ejecuta la suite resultante
    runner = unittest.TextTestRunner(verbosity=2)
    runner.run(suite_descubierta)

if __name__ == "__main__":
    ejecutar_todas_las_pruebas()

Etiquetas: Unittest Python Selenium testing automation

Publicado el 6-21 05:34