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!!

Última modificación: 16/02/2021

Autor