62

Cómo hacer mejores tests en Python usando Mock

29762Puntos

hace 3 años

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

¿Qué son los 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.

¿Cómo se hacen tests en Python?

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

python mocks 1.png

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.

Qué son los mocks

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.

python mocks 2.png

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:

python mocks 3.png

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.

Cómo usar un mock

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:

python mocks 4.png
  • Con MagicMock y patch crearemos nuestro objeto de pruebas.
  • TestCase es la clase de la que extenderemos nuestros test.
  • get_nacionality _by_name es la función que queremos testear.

En la definición de nuestro test usamos:

  • Un decorador: línea que empieza con @ y que sirve para cambiar el comportamiento de una función, esta clase te lo explica mejor.
  • Patch: es el que nos permite reemplazar el comportamiento de un objeto con un mock. En este caso el objeto get del módulo requests.
  • Un parámetro mock: aquí lo llamamos mock_requests. Básicamente, este parámetro es el objeto retornado por patch para simular el objeto que le hayamos indicado
python mocks 5.png

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

¿Cómo sabe el mock qué hacer?

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:

python mocks 6.png

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

python mocks 7.png

Hasta aquí parece que todo se puso confuso, entonces hagamos un recuento:

  • Con el decorador patch indicamos el método que no queremos que nuestro test ejecute de verdad sino que simule su comportamiento y lo manejamos con el parámetro mock_requests
  • Con MagicMock indicamos las características de la respuesta que nos entrega el método get. En este caso un objeto que cuenta con un atributo text que es el que nos interesa

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:

python mocks 8.png

El test completo que acabamos de hacer se ve así:

python mocks 9.png

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.

Cómo correr los tests

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.

David
David
davidjaras

29762Puntos

hace 3 años

Todas sus entradas
Escribe tu comentario
+ 2
Ordenar por:
7
31536Puntos

Hace falta de verdad, mucho la escuela de QA.

5
15897Puntos

Wow. Lo pondré aprueba en seguida
Es uno de los mejores posts que he visto en el blog de Platzi.

3
28180Puntos

Estoy tomando el curso de Backend con Django y esto es muy útil, gracias de verdad!

2
11988Puntos

Cómo correr el test?

1
29762Puntos
3 años

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

1
12338Puntos

Queremos el curso de tests con python!!!

1
4719Puntos
3 años

Un curso de PyTest tal vez?

1
12338Puntos
3 años

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.