Conoce cómo hacer mejores tests en Python utilizando mock con esta guía completa.
Empezar a programar es una de las aventuras más fascinantes y aprender un lenguaje nuevo es como adquirir un superpoder. Algo así como obtener una gema del infinito.
Nos empezamos a familiarizar con el entorno, la sintaxis del lenguaje y sus aplicaciones. Todo es risas y diversión hasta que tenemos nuestro primer proyecto por el que nos van a pagar y leemos el requerimiento: “se debe entregar el proyecto con tests”
Los tests son código que escribimos para comprobar que lo que estamos programando funciona como debería. Existen muchos tipos de test, este post está relacionado a unit test o pruebas unitarias, que se refieren a la comprobación individual de funciones y métodos. Tenemos también un artículo más a fondo sobre qué son los unit tests.
Python trae por defecto la librería unittest que incluye todos los métodos y módulos necesarios para hacer las validaciones en nuestro código. A continuación te muestro un ejemplo de un test para una función que suma dos números
Ahora todo sigue siendo risas y diversión, porque nos decimos por dentro “no hay problema, no se ve difícil” y acudimos al ejemplo anterior de la función Suma y creemos que vamos a estar bien.
Pero, por ejemplo: ¿qué pasa si la función que queremos testear hace una consulta a una API? Nuestro test podría fallar si la API no está disponible, si falla la conexión e incluso si la API fue actualizada causando que la respuesta sea distinta.
Nuestras pruebas unitarias no deben estar ligadas a esta clase de situaciones, ya que el objetivo de esta clase de tests es comprobar que un segmento específico del código funcione correctamente.
Si tienes dudas sobre lo que es una API, puedes saber qué es y cómo se usa en el curso de Introducción al desarrollo backend.
Los mocks son objetos “dummy” (o de muestra) con los que podemos simular objetos cuyo funcionamiento es más complejo. No es recomendable utilizar el objeto real como parte de la prueba.
¿Cómo así?
Veamos el siguiente ejemplo:
En el superproyecto con el que vas a empezar a ganar tus primeros dólares como desarrollador de Python, debes hacer una petición (request, en lenguaje developer) a la API de nationalize.io con el fin de retornar la nacionalidad más probable de un nombre.
Este es una muestra de la consulta a la API, para este caso, la nacionalidad más probable para el nombre “David” es US.
Supongamos que ya tenemos un entorno virtual y algunas librerías instaladas: (ver curso de python) y tienes la siguiente función:
Con este código obtenemos un nombre como parámetro y retornamos su nacionalidad. Más adelante lo explicaré con más detalle.
Ahora bien, el objeto requests es un buen ejemplo de cuando no es recomendable incluir un elemento en los test: Su respuesta puede variar por condiciones del entorno como estado de la conexión a internet, respuesta de la API, entre otros. Todo esto puede hacer que el test falle, así la lógica de tu algoritmo esté bien.
En este caso el test se debe centrar en que hace el código con la respuesta correcta y no si requests funciona en ese momento en particular.
Por fin, vamos a hacer algo divertido, utilizaremos un mock como simulación de un requests para verificar que todo esté ok con nuestra lógica.
Creamos nuestro archivo de test <code>…tests/test_get_nacionality.py </code>
Incluimos las librerías que vamos a emplear:
En la definición de nuestro test usamos:
En resumen: Al pasar requests y su método get al decorador patch le indicamos a python: hey cuando veas un get de la librería requests no lo ejecutes, más bien remplaza su comportamiento con el parámetro mock_requests
Buena pregunta. Y la respuesta es que no lo sabe. Vamos a decirle qué tiene que hacer.
Veamos nuevamente la función que queremos testear:
En esta función hacemos el llamado a la API, guardamos la respuesta en texto plano, puesto que es la forma más general de hacerlo y procesamos el resultado de acuerdo a nuestras necesidades. En este caso, transformamos el texto en json para después retornar el código de país de la nacionalidad más probable.
Como nuestro mock es sobre el método get, debemos indicarle el atributo text con un valor semejante al de la respuesta de la API.
Aquí es donde usamos MagicMock, que nos permite crear un objeto de prueba en el que podemos definir atributos, valores a retornar, entre otras cualidades, para así evitar crear un objeto real. Nosotros simularemos el objeto de respuesta que entrega requests.get y definiremos su atributo text
Hasta aquí parece que todo se puso confuso, entonces hagamos un recuento:
Y nos falta lo más simple, indicarle a mock_requests cuál es la respuesta que debe retornar, llamar la función get_nacionality_by_name y probar el resultado:
El test completo que acabamos de hacer se ve así:
Con esto ya podremos correr nuestro test independiente del objeto requests real. Recuerda: estamos probando la lógica de la función, no si realiza bien la consulta.
Desde el directorio principal del proyecto, puedes correr el archivo de test con:
python -m unittest tests/test_get_nacionality.py
Y si llegas a tener muchos archivos de test y quieres ejecutarlos todos, puedes usar:
python -m unittest discover tests
Ahora ya sabes cómo hacer tests de código con mayor complejidad para que puedas escribir código aún más profesional. Recuerda tomar nuestro curso de Python y seguir desarrollando tus habilidades en este maravilloso lenguaje. Nunca pares de aprender.
Hace falta de verdad, mucho la escuela de QA.
Wow. Lo pondré aprueba en seguida
Es uno de los mejores posts que he visto en el blog de Platzi.
Muy interesante.
Estoy tomando el curso de Backend con Django y esto es muy útil, gracias de verdad!
exelente
Cómo correr el test?
Hola Luis,
Desde el directorio principal del proyecto puedes correr el archivo de test con
>python -m unittest tests/test_get_nacionality.py
Y si llegas a tener muchos archivos de test y quieres ejecutarlos todos puedes usar
>python -m unittest discover tests
Queremos el curso de tests con python!!!
Un curso de PyTest tal vez?
si es con pytest mejor, la idea sería tener un proyecto (ejemplo una API) y tener los tests a nivel clases, a nivel configuraciones y los endpoints.
Sumandole un plus: usando docker y tener por etapas, si pasa los tests que se construya la imagen.