En este post vamos a ver cómo iniciarse con unittest, una de las librerías más populares para hacer tests unitarios en Python. Si primero quieres hacer un repaso rápido a este concepto, puedes mirar primero esta introducción al testing.
Introducción a unittest
El framework de testing unittest funciona de forma similar a la mayoría de librerías para hacer tests unitarios. Estos son los conceptos clave:
- configuración del test (fixture): se llama fixture a la preparación del contexto para las pruebas, como la creación de bases de datos temporales, carpetas o servicios.
- caso de test (TestCase): es la parte que comprueba o verifica el resultado obtenido en un test a partir de una serie de parámetros de entrada. Se usa la clase TestCase para crear cada test unitario.
- conjunto de tests: es una colección de casos y/o de conjuntos de tests que deben ejecutarse de forma conjunta.
- ejecutor de tests: es el componente que ejecuta los tests de forma organizada y da el resultado de las pruebas.
Para crear un test es necesario usar una clase que herede de unittest.TestCase. Los métodos que se definan en esta clase, por convención empiezan por "test", esto sirve para indicar al ejecutor de test qué métodos son pruebas. Dentro de cada test se tienen disponibles varias funciones para verificar el resultado de las operaciones que se hagan.
Un ejemplo sencillo
Vamos a ejecutar varios tests en algunos métodos que tenemos disponibles para manipular listas.
import unittest class TestMetodosListas(unittest.TestCase): """ Clase principal donde definiremos los tests Vamos a probar unos cuantos de los métodos disponibles en listas: append, clear, copy y count. """ def test_append(self): """ Creamos una lista, guardamos su largada y comprobamos que al añadir un nuevo elemento su largada se incrementa en 1 """ l = [1, 2, 3] current_length = len(l) l.append(4) new_length = len(l) self.assertEqual(new_length, current_length + 1) def test_clear(self): """ Creamos una lista y comprobamos que la largada de la misma al ejecutar clear() es 0 """ l = [1, 2, 3] l.clear() length = len(l) self.assertEqual(length, 0) def test_copy(self): """ Creamos una lista y una copia de la misma para compararlas """ l = [1, 2, 3] l2 = l.copy() self.assertEqual(l, l2) def test_count(self): """ Creamos una lista donde un elemento sale 2 veces y comprobamos que así es con count() """ l = [1, 2, 3, 1] n1 = l.count(1) self.assertEqual(n1, 2) # esta es una forma sencilla de empezar # los tests cuando ejecutemos el archivo if __name__ == "__main__": unittest.main()
Si ejecutamos el archivo, la librería ejecutará los tests y nos lanzará el resultado, algo como esto:
.... ---------------------------------------------------------------------- Ran 4 tests in 0.001s OK
¿Pero qué pasa si falla alguno de los tests? Modifica una parte de los assertEqual() para provocar un error y ejecuta de nuevo el archivo:
def test_clear(self): """ Creamos una lista y comprobamos que la largada de la misma al ejecutar clear() es 0 """ l = [1, 2, 3] l.clear() length = len(l) self.assertEqual(length, 1) # la largada nunca será 1
.F.. ====================================================================== FAIL: test_clear (__main__.TestMetodosListas) Creamos una lista y comprobamos que la largada ---------------------------------------------------------------------- Traceback (most recent call last): File "C:\Users\polmo\Desktop\testing\test_lists.py", line 26, in test_clear self.assertEqual(length, 1) AssertionError: 0 != 1 ---------------------------------------------------------------------- Ran 4 tests in 0.001s FAILED (failures=1)
Ahí veremos qué falla y por qué.
Conclusiones
Unittest nos ofrece una forma muy simple de desarrollar tests para poner a prueba nuestras aplicaciones en Python. Solo hemos visto el método assertEqual(), pero hay otros similares para hacer comprobaciones del estilo, puedes ver un listado en la documentación.
Una vez tenemos las pruebas hechas, ejecutarlas también es sencillo y, cuando algo falla, nos lo indica de forma precisa. Con unittest tenemos una librería sencilla pero completa, que nos permitirá cubrir la mayoría de necesidades del testing unitario de aplicaciones.