Tras el artículo Python: Cómo hacer pruebas es lógico pensar que vendrá la segunda parte.

En esta ocasión nos centraremos en UnitTest.

Construcción básica

Hay muchas ocasiones en las que las pruebas realizadas con doctest se nos quedarán cortas. Por ejemplo, ¿cómo probamos un acceso a base de datos? ¿Y una interacción entre clases? Con este fin está unittest. Hay gente que lo denomina “PyUnit”, ya que es similar a JUnit.

Unittest nos ofrece toda la potencia del lenguaje para probar nuestros programas. De esta manera, escribiremos “Casos de pruebas” que podremos agrupar en “Suites de pruebas”. Además, nos permitirá agrupar funcionalidad común, de manera que preparemos nuestro entorno (a esto se le conoce como “fixture”).

Como en el tutorial anterior, vamos a poner un ejemplo y a probarlo. Voy a utilizar la función factorial del otro ejemplo que ya sabemos que funciona:

import unittest

def factorial(n):
    return 1 if n < 1 else n * factorial(n-1)

class tester (unittest.TestCase):
    def test_1(self):
        self.assertEqual(1, factorial(1))

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

Como podemos ver, creamos una clase que hereda de unittest.TestCase, ya que es un caso de pruebas. Python facilita la tarea de crear numerosas clases y así aislar los casos de uso que necesitemos, a menudo con una inicialización diferente. A continuación vienen los tests, que deben comenzar por la palabra “test” o no se ejecutarán.

En el main ejecutamos el programa principal de unittest, lo que permitirá descubrir los tests de la clase en la que estamos. Esto se puede hacer en línea de órdenes mediante (sólo a partir de Python 2.7):

$ python -m unittest MODULO_A_PROBAR_SIN_EXTENSIÓN

Dicotomía de un test

Los tests, por lo general, constan de 3 partes que recomiendo diferenciar:

  • Inicialización, que es donde se establecen valores necesarios para nuestros tests. Debe ser pequeña. A menudo ni siquiera es necesaria y se elimina. Por ejemplo, puede ser la construción del SUT (Subject Under Test).
  • Ejecución, que es la línea en la que se llama a la funcionalidad a probar. No debe ocupar más de una línea.
  • Comprobación, que es el test propiamente dicho, expresado en forma de verificaciones.

Veamos el test desglosado:

class tester (unittest.TestCase):
    def test_1(self):
        # inicialización (vacío)
        result = factorial(1)    # ejecución
        self.assertEqual(1, result)  # comprobación

Funciones especiales

Hay algunas funciones especiales, que son:

  • setUp, que permite inicializar la fixture, es decir, establecer un entorno de pruebas para cada prueba. Se ejecutará siempre antes de cada test.
  • tearDown, que realiza la operación contraria, es decir, se ejecuta siempre después de cada test.
  • setUpClass, que se ejecuta una única vez al comienzo de la clase.
  • tearDownClass, que se lanza cuando se han terminado todos los tests.

Comprobaciones

Hay muchos tipos de comprobaciones a realizar. Se puede comprobar si el resultado es igual, menor, mayor, contiene, etc. el valor esperado o, incluso, si lanza una excepción.

Hay escuelas de pensamiento que opinan que un test debe contener una única verificación. No es un grupo de radicales, sino que tiene su sentido: los tests nos evitan tener que depurar, y si el nombre del test está bien elegido, un fallo en una comprobación nos dirá exactamente dónde está el problema. Si tenemos varias comprobaciones en un test, esta comprobación no será inmediata.

Realmente Python nos facilita esta tarea, ya que si necesitamos una nueva comprobación, podemos desglosarlo en una clase nueva, de manera que en la inicialización de la clase establezcamos la sección de inicialización del test y dejar para las funciones sólo las comprobaciones.

Comprobaciones adicionales

Si necesitamos realizar comprobaciones más complejas, podemos utilizar módulos dedicados a ello, como Hamcrest.

Ineficiente

Alguien puede pensar que es peor utilizar Unittest frente a doctest. En algunos casos, esta afirmación es totalmente cierta, como en el caso de la función factorial. Sin embargo, doctest no ofrece la versatilidad que permite unittest. Esto quedará demostrado en la siguiente entrega de Python: Cómo hacer pruebas, donde estudiaremos herramientas que nos faciliten las pruebas, y seguiremos con otro tutorial más con los dobles o _mock_s, que facilitarán aún más la creación de pruebas.

Más información

Podemos encontrar más información en la web de unittest.