Pythonist Development

Testeando Django Rest Framework de manera correcta

Como desarrolladores django es común tener que testear DRF, pero no siempre lo hacemos de manera correcta, terminamos por crear tests unitarios lentos, no aislados, que nos dificulta encontrar errores y mantener un coverage alto, en este post tratare de explicar de manera fácil

como testear utilizando pytest como reemplazo a la librería por defecto de django. Durante todo el proceso, usare las siguiente premisas:

  1. Usare la librería pytest para los test unitarios.
  2. Los tests unitarios no tendrán acceso a la BD.
  3. Utilizaremos pytest-mock para "Mockear" los métodos que no nos interese testear, como las cosas propias del framework que vienen ya testeadas por los desarrolladores del framework.

¿Porque los test unitarios no deberían tener acceso a la BD?

Muchos autores han discutido, acerca de esto a través de los años, en este momento, dare mi opinión personal, basada es mi experiencia como desarrollador web.

Mientras tu proyecto crece, la creación de fixtures, para la gran cantidad de modelos, se volverá increíblemente molesta, la gran cantidad de migraciones migraciones incrementará el problema, por supuesto, hacer squash de las migraciones cada cierto tiempo puede aminorar esta carga, pero aun así muchas veces es complicado, todo esto en conjunto tiene un ineludible final, el tiempo de ejecución de los tests crecerá vertiginosamente, por lo que sera un problema para el CI/CD de tu proyecto, y una perdida de tiempo para el equipo en general.

Por otro lado los test sin acceso a BD nos permite testear de manera aislada.

¿Porque deberían tus tests unitarios ser aislados?

Aislar tus test unitarios es una de las muestras de que tu código esta desacoplado por lo que estas siguiendo el principio de Unix, has que cada pieza de tu código hace una sola cosa y la hace bien, lo que por supuesto ayuda a tener un código mas mantenible. Acá te dejo algunas de las ventajas de aislar tus test unitarios:

  • Sera fácil encontrar nuestros errores.
  • No dependeremos de la BD, por lo que nuestros tests serán muy rápidos.
  • Facilitaremos el despliegue de la aplicación.-
  • Comprenderemos como funciona el framework mas a profundidad.
  • Al empezar a testear de esta manera, nos obligaremos a crear código más "testeable", por lo que sera mas legible, organizado y limpio.

¿ Que es Mocking ?

Es un "Doble de Prueba" nos permite Fakear objectos, funciones para que se retornen un valor predeterminados por nosotros, existen varios tipos de Mocking y normalmente los englobamos todos con el termino Mock, si quieres detallar mas los tipos, te recomiendo este articulo.

En este post usare la librería pytest-mock y django-mock-queries, para Fakear QuerySets de django, y omitir el acceso la BD, con lo que logramos test, que no dependan de la bd y que se ejecutan en micro segundos.

Sabiendo esto, ¿que testeamos?

Por lo regular al desarrollar una API Rest usando DRF se deben testear, estos componentes:

  1. Serializer.
  2. View.

A esto debemos añadirle cosas comunes del framework, como:

  • Helpers, UseCase, Services (Como llamen en tu equipo a la capa de la lógica del negocio).
  • Métodos del manager del modelo.
  • Métodos del modelo.

Estos últimos debemos evitar usarlos, ya que el modelo debería ser usado solo como capa de acceso a datos, no una capa de lógica del negocio, como muchos (y me incluyo) en nuestros inicios, comentemos el error de usar.

El modelo.

Para este caso de estudio, crearemos una pequeña API REST, para gestión de mi garage personal, en el cual solo tendremos un pequeño modelo llamado Car.

Managers

Los managers de Django nos permiten separar la lógica de negocio de los modelos y como bono extra nos ayudan a mantener el principio DRY(Don't repeat yourself).

Continuando con el ejemplo imaginemos que queremos obtener todos los autos cargados al sistema en una fecha especifica (Si, si tengo un inmenso garage imaginario), para esto debemos usar el ORM de Django y ejecutar esta consulta:

Car.objects.filter(created=my_date).order_by('year')

Sabiendo esto y para no repetir código a lo largo de nuestra aplicación con el objetivo de facilitar el testing, creamos un manager, y agregamos un nuevo metodo get_cars_by_created

Test:

Serializer

Test:

Vista

Test:

Ahora podemos ver el coverage de nuestra aplicación:

Coverage

Y eso es todo, con esto tendremos un coverage 100% en nuestra app, testeamos de manera separada, todo nuestro código e hicimos del mundo un lugar mejor, si quieren ver el repositorio completo aquí esta