Data lovers blog by Admetricks

Especial Data Lover: Cómo hacer mejores tests en Python usando Mock

Para algunos 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

Paso 1: ¿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.

¿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.

Paso 2: ¿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.

Paso 4: ¿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.
  • 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

Paso 5: ¿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.

Paso 6: ¿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

Gracias a David, ahora ya sabes cómo hacer tests de código con mayor complejidad para que puedas escribir código aún más profesional y ser el mejor data lover. 

Agradecemos a Platzi por difundir el contenido de David https://platzi.com/blog/tests-python-usando-mock/, también les dejamos su usuario platzi https://platzi.com/p/davidjaras/

Suscríbete