Pruebas Unitarias para Clases en Python con unittest

Validación de Clases mediante Pruebas Unitarias

Probar el comportamiento de las clases es fundamental en el desarrollo orientado a objetos. A diferencia de las pruebas de funciones aisladas, el testeo de clases implica verificar que los métodos interactúen correctamente con los atributos del objeto y mantengan el estado esperado. Al contar con una suite de pruebas robusta, podemos refactorizar el código interno de una clase con la seguridad de que no romperemos las funcionalidades existentes.

Herramientas de Aserción en unittest

El módulo unittest de Python proporciona la clase TestCase, la cual incluye diversos métodos de aserción. Estas herramientas permiten comprobar si las condiciones de ejecución coinciden con los resultados previstos. Si la condición se cumple, la prueba continúa; de lo contrario, se lanza una excepción indicando el fallo.

Algunas de las aserciones más utilizadas incluyen:

  • assertEqual(a, b): Verifica que a == b.
  • assertNotEqual(a, b): Verifica que a != b.
  • assertTrue(x): Verifica que x es verdadero.
  • assertFalse(x): Verifica que x es falso.
  • assertIn(item, list): Verifica que el elemento se encuentra en la colección.
  • assertNotIn(item, list): Verifica que el elemento no está en la colección.

Definición de una Clase para Pruebas

Para ilustrar el proceso, utilizaremos una clase denominada EncuestaInteractiva, diseñada para recopilar y gestionar respuestas a una pregunta específica.

class EncuestaInteractiva:
    """Gestiona la recopilación de respuestas para una encuesta específica."""

    def __init__(self, enunciado):
        """Almacena el enunciado y prepara la lista de datos."""
        self.enunciado = enunciado
        self.respuestas_obtenidas = []

    def mostrar_pregunta(self):
        """Imprime la pregunta de la encuesta."""
        print(self.enunciado)

    def registrar_respuesta(self, nueva_entrada):
        """Añade una respuesta a la colección."""
        self.respuestas_obtenidas.append(nueva_entrada)

    def ver_resumen(self):
        """Muestra todos los datos recolectados."""
        print("Resultados registrados:")
        for r in self.respuestas_obtenidas:
            print(f"-> {r}")

Esta clase permite instanciar un objeto con una pregunta, añadir respuestas individuales mediante registrar_respuesta() y visualizar el conjunto de datos mediante ver_resumen().

Implementación de Pruebas para la Clase

A continuación, desarrollamos un caso de prueba para validar que una respuesta enviada se almacene correctamente dentro de la lista interna del objeto. Utilizaremos el método assertIn() para esta comprobación.

import unittest

class TestEncuestaInteractiva(unittest.TestCase):
    """Pruebas unitarias para la clase EncuestaInteractiva."""

    def test_almacenamiento_individual(self):
        """Verifica que una única respuesta se guarde con éxito."""
        pregunta = "¿Qué sistema operativo prefieres?"
        encuesta = EncuestaInteractiva(pregunta)
        encuesta.registrar_respuesta('Linux')
        
        self.assertIn('Linux', encuesta.respuestas_obtenidas)

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

En este bloque, creamos una instancia de EncuestaInteractiva, simulamos la entrada de un dato y confirmamos que dicho dato aparezca en el atributo respuestas_obtenidas. Al ejecutar este script, unittest reportará si la prueba fue exitosa.

Optimización con el Método setUp()

Cuando las pruebas crecen en complejidad, es común notar redundancia al instanciar la misma clase en cada método de prueba. La clase unittest.TestCase ofrece el método especial setUp(), el cual se ejecuta automáticamente antes de cada función de prueba (aquellas que comienzan con test_).

Esto permite inicializar objetos y definir datos de prueba una sola vez, dejándolos disponibles como atributos de la instancia (self).

import unittest

class TestEncuestaInteractivaOptimizado(unittest.TestCase):
    """Suite de pruebas optimizada utilizando setUp."""

    def setUp(self):
        """Configuración inicial para todos los métodos de prueba."""
        pregunta_test = "¿Cuál es tu lenguaje de programación principal?"
        self.mi_encuesta = EncuestaInteractiva(pregunta_test)
        self.muestras = ['Python', 'Rust', 'C++']

    def test_guardar_dato_unico(self):
        """Prueba la inserción de un solo elemento."""
        self.mi_encuesta.registrar_respuesta(self.muestras[0])
        self.assertIn(self.muestras[0], self.mi_encuesta.respuestas_obtenidas)

    def test_guardar_multiples_datos(self):
        """Prueba la inserción de múltiples elementos de forma secuencial."""
        for item in self.muestras:
            self.mi_encuesta.registrar_respuesta(item)
        
        for item in self.muestras:
            self.assertIn(item, self.mi_encuesta.respuestas_obtenidas)

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

El uso de setUp() limpia el código de los métodos de prueba, permitiendo que se enfoquen exclusivamente en la lógica de validación. Cualquier cambio en la estructura de inicialización de la clase ahora solo requiere una modificación en un único lugar, facilitando el mantenimiento de la suite de pruebas.

Etiquetas: Python Unittest testing oop Software-Engineering

Publicado el 10-9 15:40