Pruebas unitarias en python
Cuando hablamos de desarrollo de software, las pruebas unitarias son un componente esencial en un proyecto, puesto que estas nos ayudan en varias cosas entre ellas a evitar que cuando se actualice algún componente del código de nuestra aplicación esta caiga en comportamientos imprevistos. En este artículo no les voy a hablar sobre TDD y cómo debe ser aplicado, en contraste les voy a comentar cómo escribir pruebas unitarias con el lenguaje python usando la librería interna unittest.
Para realizar dichas pruebas he tomado como ejemplo la aplicación de ToDo's que realizamos en Python + Flask.
Lo primero que vamos a hacer es crear una carpeta llamada test en el directorio de nuestra aplicación, la cual debe quedar así:
app/
config.py
README.md
requirements.txt
run.py
test/
Puesto que test es un paquete de python el mismo debe tener un archivo llamado init.py
.
Para que las pruebas funcionen correctamente cada archivo de pruebas unitarias debe comenzar con el prefijo
test_
Ejemplo:
test_hola_mundo.py
En nuestra carpeta test vamos a crear un archivo llamado test_password_utils.py
con el cual vamos a probar que nuestra función get_hashed_password_with_sha512
esté haciendo correctamente el hash con sha512 a cualquier texto que le enviemos
El código de nuestra prueba debe ir de la siguiente manera:
# Importamos la librería unittest
import unittest
# Importamos la librería hashlib
import hashlib
# Importamos nuestra función get_hashed_password_with_sha512 del archivo password_utils.py
from app.utils.password_utils import get_hashed_password_with_sha512
# Creamos una clase para la prueba unitaria, la misma que debe heredar de unittest.TestCase
class GetSha512Test(unittest.TestCase):
# Creamos nuestras funciones para probar el código escrito
def test_work_if_is_sha512(self):
# Creamos un resultado esperado y lo asignamos a la variable "expected"
expected = hashlib.sha512("Hello".encode('utf-8')).hexdigest()
# Llamamos a nuestra funcion, le mandamos el mismo texto,
# en este caso "Hello" y le asignamos a la variable "acutal"
actual = get_hashed_password_with_sha512("Hello")
# Hacemos la comprobación de que "expected" y "actual" sean iguales
self.assertEqual(expected, actual)
def test_fail_if_not_sah512(self):
# Creamos un resultado esperado utilizando "sha1"
expected = hashlib.sha1("Hello".encode('utf-8')).hexdigest()
# Llamamos a nuestra funcion con el mismo texto que el esperado
actual = get_hashed_password_with_sha512("Hello")
# En este caso como "sha1" y "sha512" son diferentes
# la comprobación debe ser hecha a que no sean iguales
self.assertNotEqual(expected, actual)
Las funciones que realizan las pruebas deben comenzar con el prefijo
test_
Ahora vamos a correr nuestras nuestras pruebas, para ello corremos el siguiente comando dentro de la carpeta de nuestra aplicación:
python -m unittest
El resultado del mismo será similar al siguiente:
Ran 2 tests in 0.000s
OK
Dice que ha corrido dos pruebas, las cuales son las dos funciones que hicimos en nuestra clase de pruebas
Debemos correr el comando con nuestro entorno virtual activado, puesto que el mismo tiene las dependencias de python que son necesarias para nuestro proyecto
Si una prueba falla, el resultado será similar al siguiente:
F.
======================================================================
FAIL: test_fail_if_not_sah512 (test.test_password_utils.GetSha512Test)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/run/media/danny/Datos/programacion/python/flask/simple-todo-with-auth/test/test_password_utils.py", line 16, in test_fail_if_not_sah512
self.assertNotEqual(expected, actual)
AssertionError: '3615f80c9d293ed7402687f94b22d58e529b8cc7916f8fac7fddf7fbd5af4cf777d3d795a7a00a16bf7e7f3fb9561ee9baae480da9fe7a18769e71886b03f315' == '3615f80c9d293ed7402687f94b22d58e529b8cc7916f8fac7fddf7fbd5af4cf777d3d795a7a00a16bf7e7f3fb9561ee9baae480da9fe7a18769e71886b03f315'
----------------------------------------------------------------------
Ran 2 tests in 0.000s
FAILED (failures=1)
En este caso he hecho que la prueba que "falla" tenga como resultado esperado sha512, el cual es el mismo que utilizamos para realizar el hash en nuestra aplicación, por lo cual el error nos dice que esperabamos que los resultados sean diferentes, pero en realidad son iguales.
Ahora vamos a probar nuestro utilitario que revisa si un request
es json caso contrario aborta la operación con un código de error 400.
# Importamos la librería unittest
import unittest
# Importamos la funcion "is_not_json_request" de nuestro archivo "json_utils.py"
from app.utils.json_utils import is_not_json_request
class JsonUtilsTest(unittest.TestCase):
def test_string_is_not_json(self):
# Puesto que string no es json presenta un "AttributeError"
# Para ello decimos que la prueba va a levantar una excepción
with self.assertRaises(AttributeError) as ex:
# Ejecutamos nuestra función con el string "Hello"
is_not_json_request("Hello")
# Hacemos la comparación de que el error que la función
# va a levantar debe contener el mismo mensaje que el que enviamos
self.assertEqual("'str' object has no attribute 'json'" in ex.exception)
En python existen otros fameworks para realizar pruebas unitarias, entre ellos están:
La URL donde se encuentra este ejemplo es: https://github.com/nano-bytes/flask/tree/master/simple-todo-with-auth
Esto ha sido todo por hoy, nos vemos en una próxima entrega.
Happy Hacking!!