Django REST Framework, normalmente abreviado como DRF, es el kit de herramientas estándar para construir APIs sobre Django. Te proporciona serializadores, viewsets, enrutadores y una capa de autenticación. Lo que también te ofrece, y que muchos desarrolladores subutilizan, es una sólida capa de pruebas que se basa en el propio ejecutor de pruebas de Django.
Esta guía muestra cómo probar una API de DRF de dos maneras. Primero, pruebas automatizadas escritas en Python usando APITestCase y APIClient de DRF, que se ejecutan sin un servidor en vivo y detectan regresiones en cada commit. Segundo, ejercitando los puntos finales en ejecución con un cliente de API, que es cómo se explora el comportamiento y se verifica el servicio real. Ambos importan y detectan problemas diferentes.
Configurar el proyecto y el entorno de prueba
Comienza con un entorno aislado para que las dependencias de prueba se mantengan separadas del resto de tu sistema:
python -m venv venv
source venv/bin/activate
pip install django djangorestframework coverage
Un proyecto DRF se prueba como cualquier proyecto Django. Por convención, las pruebas residen en un archivo tests.py dentro de cada aplicación, o en un paquete tests/ si tienes muchas. El ejecutor de pruebas de Django descubre cualquier clase que herede de una variante de TestCase y cualquier método cuyo nombre comience con test_.
La elección clave es qué clase base usar. El unittest.TestCase puro no tiene base de datos. El TestCase de Django envuelve cada prueba en una transacción y la revierte, para que las pruebas no se contaminen entre sí. El APITestCase de DRF extiende el TestCase de Django e intercambia el cliente por el APIClient de DRF, que entiende la autenticación y los tipos de contenido de DRF. Para el trabajo con API, usa APITestCase.
Hay una clase más que vale la pena conocer. TransactionTestCase no envuelve las pruebas en una transacción, lo cual necesitas cuando el código bajo prueba gestiona las transacciones por sí mismo o se basa en características de la base de datos que una transacción envolvente ocultaría. Es más lento porque trunca las tablas entre pruebas en lugar de revertirlas, así que úsalo solo cuando TestCase genuinamente no pueda modelar el comportamiento. Para la gran mayoría de las pruebas de endpoints, APITestCase es la elección correcta y más rápida.
También ayuda pensar en los datos de prueba desde el principio. Usa un método setUp para crear las filas que necesita cada prueba en una clase, o setUpTestData si los datos son de solo lectura y se pueden compartir entre la clase para mayor velocidad. Para proyectos más grandes, una librería de fábrica como factory_boy genera instancias de modelo válidas sin que tengas que especificar cada campo, lo que mantiene las pruebas cortas y resistentes cuando un modelo adquiere un nuevo campo requerido.
Probar serializadores como unidades
Los serializadores realizan validación y convierten entre instancias de modelo y JSON. Son pequeños y puros, lo que los hace ideales para pruebas unitarias rápidas que no tocan los endpoints.
Supongamos que tienes un modelo Article y un ArticleSerializer. Una prueba unitaria verifica que los datos válidos pasan y los datos inválidos fallan:
from django.test import TestCase
from articles.serializers import ArticleSerializer
class ArticleSerializerTests(TestCase):
def test_valid_data_passes(self):
data = {"title": "Caching strategies", "body": "Use ETags."}
serializer = ArticleSerializer(data=data)
self.assertTrue(serializer.is_valid())
def test_missing_title_fails(self):
data = {"body": "No title here."}
serializer = ArticleSerializer(data=data)
self.assertFalse(serializer.is_valid())
self.assertIn("title", serializer.errors)
Estas pruebas se ejecutan en milisegundos porque no hay HTTP ni vista. Te indican que las reglas de validación son correctas incluso antes de considerar el endpoint. Esta cobertura a nivel de unidad es la base de la pirámide de pruebas descrita en qué son las pruebas automatizadas.
Probar endpoints con APITestCase y APIClient
Las pruebas de endpoint verifican la ruta completa: enrutamiento, la vista, el serializador y la respuesta. El APIClient de DRF envía solicitudes en proceso, por lo que no se ejecuta ningún servidor y las pruebas se mantienen rápidas.
from django.urls import reverse
from rest_framework import status
from rest_framework.test import APITestCase
from articles.models import Article
class ArticleEndpointTests(APITestCase):
def setUp(self):
Article.objects.create(title="First post", body="Hello world")
def test_list_articles_returns_200(self):
url = reverse("article-list")
response = self.client.get(url)
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(len(response.data), 1)
def test_create_article(self):
url = reverse("article-list")
payload = {"title": "Second post", "body": "More content"}
response = self.client.post(url, payload, format="json")
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
self.assertEqual(Article.objects.count(), 2)
Algunos detalles que vale la pena mencionar. self.client es una instancia de APIClient proporcionada por APITestCase. Usa reverse() con el nombre de la ruta en lugar de codificar las URLs, para que un cambio de ruta no rompa cada prueba. Pasa format="json" en las escrituras para que el cliente serialice la carga útil correctamente. Afirma sobre el código de estado con las constantes nombradas de rest_framework.status, ya que se leen más claramente que los números brutos. Los códigos de estado en sí mismos están cubiertos en la guía sobre qué códigos de estado HTTP deben usar las APIs REST.
Probar autenticación y permisos
La mayoría de las APIs reales protegen sus endpoints. Necesitas pruebas que demuestren que las solicitudes no autenticadas son rechazadas y que las solicitudes autenticadas con los permisos correctos tienen éxito.
APIClient ofrece dos formas de autenticar una prueba. force_authenticate() omite la verificación de credenciales y adjunta un usuario directamente, lo cual es ideal para probar la lógica de la vista en sí. login() o credentials() ejercitan la ruta de autenticación real. Aquí hay una prueba de permisos usando ambas direcciones:
from django.contrib.auth.models import User
from rest_framework import status
from rest_framework.test import APITestCase
from django.urls import reverse
class ArticlePermissionTests(APITestCase):
def setUp(self):
self.user = User.objects.create_user(
username="editor", password="testpass123"
)
self.url = reverse("article-list")
def test_anonymous_cannot_create(self):
payload = {"title": "Blocked", "body": "Should fail"}
response = self.client.post(self.url, payload, format="json")
self.assertEqual(
response.status_code, status.HTTP_403_FORBIDDEN
)
def test_authenticated_user_can_create(self):
self.client.force_authenticate(user=self.user)
payload = {"title": "Allowed", "body": "Should pass"}
response = self.client.post(self.url, payload, format="json")
self.assertEqual(
response.status_code, status.HTTP_201_CREATED
)
La primera prueba confirma que la clase de permiso bloquea las escrituras anónimas. La segunda confirma que un usuario autenticado puede pasar. Juntas, fijan el límite de permisos, que es exactamente el tipo de comportamiento que se rompe silenciosamente durante las refactorizaciones. Si estás probando la autenticación de tokens específicamente, cambia force_authenticate por self.client.credentials(HTTP_AUTHORIZATION="Token " + token) para ejercitar la ruta real de la cabecera.
Vale la pena ser deliberado sobre cuál usar. force_authenticate es más rápido y aísla la lógica de la vista, por lo que se adapta a la mayor parte de las pruebas de permisos donde solo te importa si un rol puede o no hacer algo. La ruta de credenciales real es lo que quieres para un conjunto más pequeño de pruebas que prueben específicamente que el mecanismo de autenticación funciona: que un token incorrecto es rechazado, que un token caducado falla, que el endpoint de inicio de sesión emite un token utilizable. Mezclar ambos te brinda una amplia cobertura a bajo costo y una cobertura profunda donde importa.
No olvides los permisos a nivel de objeto. Muchas APIs de DRF permiten a un usuario editar sus propios registros pero no los de otra persona. Prueba eso explícitamente: crea dos usuarios, haz que uno cree un registro, luego afirma que el otro usuario obtiene un 403 o 404 al intentar modificarlo. Los endpoints de lista merecen el mismo escrutinio, ya que un queryset con fugas puede devolver filas que un usuario nunca debería ver, incluso cuando el endpoint de detalle está bloqueado.
Ejecutar el conjunto de pruebas y medir la cobertura
Ejecuta todas las pruebas con el ejecutor de Django:
python manage.py test
El ejecutor descubre tus clases de prueba, configura una base de datos de prueba desechable, ejecuta cada prueba dentro de una transacción y revierte después. Una ejecución limpia imprime OK. Un fallo muestra la aserción que se rompió y dónde.
Saber que el conjunto de pruebas pasa no es lo mismo que saber que cubre lo suficiente. La herramienta coverage mide qué líneas se ejecutaron realmente:
coverage run --source='.' manage.py test
coverage report
coverage html
El informe enumera cada archivo con el porcentaje de líneas ejercitadas. La salida HTML resalta las líneas no probadas en rojo, lo que te señala directamente las lagunas. Aspira a una cobertura significativa de vistas, serializadores y permisos en lugar de un único número destacado. El artículo sobre cómo escribir scripts de prueba automatizados cubre lo que hace que una prueba valga la pena mantener, ya que la cobertura de pruebas débiles no es una seguridad real.
Probar la API en vivo con un cliente
Las pruebas automatizadas de Python son rápidas y se ejecutan en CI, pero ejercitan la aplicación en proceso. No detectan problemas que solo aparecen en el servicio en ejecución: un encabezado CORS mal configurado, un proxy inverso que elimina algo, una base de datos lenta bajo carga real o una diferencia entre tu base de datos de prueba y la de producción. Para eso, envías solicitudes reales a los endpoints desplegados.
Apidog encaja perfectamente aquí. Es una plataforma API todo en uno, por lo que puedes importar el esquema OpenAPI de tu API DRF, enviar solicitudes en vivo a un servidor en ejecución y construir aserciones visualmente sin escribir más Python. DRF puede generar un esquema OpenAPI, y Apidog lo consume directamente, lo que mantiene tu cliente sincronizado con el contrato real. También puedes construir escenarios de prueba de varios pasos, por ejemplo, iniciar sesión, crear un artículo, obtenerlo, eliminarlo, y ejecutarlos en un horario o en CI. Esto complementa tu suite APITestCase en lugar de reemplazarla: las pruebas unitarias y de endpoints protegen el código, el cliente API protege el servicio desplegado. Puedes descargar Apidog para importar un esquema DRF y probarlo. Para equipos que prefieren quedarse en Python de principio a fin, la guía pytest API automated testing framework muestra cómo ejecutar pruebas de estilo DRF bajo pytest.
Preguntas frecuentes
¿Cuál es la diferencia entre TestCase y APITestCase?
El TestCase de Django envuelve cada prueba en una transacción de base de datos y te proporciona un cliente de prueba estándar de Django. El APITestCase de DRF lo hereda e intercambia el cliente por APIClient, que comprende los esquemas de autenticación, la negociación de contenido y el argumento format en las solicitudes de DRF. Usa APITestCase para probar los endpoints de DRF.
¿Cuándo debo usar force_authenticate en lugar de login?
Usa force_authenticate() cuando quieras probar la lógica de la vista y los permisos sin el costo y la complejidad del flujo de credenciales real. Adjunta un usuario a la solicitud directamente. Usa login() o credentials() cuando lo que quieras verificar sea el mecanismo de autenticación en sí, como la autenticación por sesión o token.
¿Las pruebas de DRF necesitan un servidor en ejecución?
No. APIClient envía solicitudes en proceso directamente a tus vistas, por lo que el conjunto de pruebas se ejecuta sin iniciar un servidor. Eso es lo que lo hace rápido. Para probar el servicio realmente desplegado, incluyendo infraestructura como proxies y CORS, envías solicitudes HTTP reales con un cliente de API como Apidog.
¿Cómo verifico la cobertura de pruebas para un proyecto DRF?
Instala el paquete coverage, luego ejecuta coverage run --source='.' manage.py test seguido de coverage report para un resumen o coverage html para una vista línea por línea. El informe HTML resalta las líneas no probadas para que puedas ver exactamente qué vistas o serializadores carecen de pruebas.
¿Debo probar los serializadores y los endpoints por separado?
Sí. Las pruebas unitarias de serializadores son rápidas y detectan errores de validación sin la sobrecarga de HTTP. Las pruebas de endpoints con APITestCase verifican el enrutamiento, los permisos y el ciclo completo de la solicitud. Mantener ambos te da una retroalimentación rápida a nivel de unidad y la confianza de que el cableado funciona a nivel de integración.
