Aprende a crear pruebas unitarias efectivas en Django REST Framework con un enfoque práctico: datos de prueba con ORM, simulación de requests con ApiClient, construcción de URL con reverse y validación de permisos que retornan 403. Verás cómo un viewset de appointments expone errores reales y cómo corregirlos rápidamente.
¿Cómo iniciar pruebas unitarias con Django REST Framework?
Para empezar, se trabaja en el archivo tests.py de la aplicación y se hereda de TestCase. La clase de pruebas define un método setup para preparar datos reutilizables y un cliente para las solicitudes. La idea es validar que el listado de appointments funcione correctamente para un viewset específico.
Crear la clase de pruebas que hereda de TestCase.
Definir setup para inicializar datos antes de cada prueba.
Preparar un paciente y un doctor con el ORM.
Instanciar self.client usando ApiClient para simular requests JSON.
¿Qué datos crear con el ORM para las pruebas?
Se importan los modelos desde pacientes y doctores y se crean instancias mínimas requeridas con el ORM de Django. Esto permite reutilizar datos en múltiples pruebas sin repetir código.
Paciente con nombre “Luis Martínez”, fecha “1990-12-05”, correo y dirección de ejemplo.
Doctor “Óscar Barajas”, ciudad “Medellín”, correo de ejemplo y sin vacaciones.
Datos en self. para que estén disponibles en cada prueba.
¿Por qué usar ApiClient para simular requests?
Se importa desde rest_framework.test y simplifica las pruebas al asumir que todo se envía como JSON por defecto. Evita definir headers en cada caso y hace el flujo más claro.
Configuración predeterminada para contenido JSON.
Menos repetición de headers y configuración.
Integración directa con asserts y respuestas del framework.
¿Cómo resolver la URL con reverse y listar appointments?
Para consultar el listado, se construye la URL con reverse usando el nombre del endpoint. Si la ruta recibe parámetros, se envían como kwargs para interpolarlos automáticamente en la URL.
Usar reverse con el nombre de la ruta.
Pasar parámetros como diccionario: id del doctor.
Enviar un GET con self.client a esa URL.
¿Cómo instalar django extensions y usar show URLs?
Se instala la extensión y se agrega a las aplicaciones instaladas para obtener el comando que lista rutas. Con esto se identifica el nombre exacto de la URL a usar con reverse.
Instalar con pip install django extensions.
Agregar a INSTALLED_APPS con guion bajo.
Ejecutar el comando show_urls para ver nombres de rutas.
¿Cómo construir la URL con parámetros?
Si la ruta requiere un parámetro (como la PK del doctor), se entrega en kwargs. Así reverse compone la URL final del listado de appointments del doctor.
Identificar el parámetro requerido por la ruta.
Enviar kwargs con el nombre del parámetro y el id del doctor.
Validar que la URL resultante sea la esperada.
¿Cómo validar códigos de estado y permisos en el viewset?
Se simula el GET y se verifican resultados con self.assertEqual sobre el status code. Primero se esperaba 200 OK, pero la respuesta mostró 403 por una política de permisos del viewset.
Hacer la solicitud con self.client.get(url)..
Comparar response.status_code con status del framework.
Usar constantes legibles: status.HTTP_200_OK o status.HTTP_403_FORBIDDEN.
¿Cómo hacer aserciones con assert equal y status?
Se importa status desde rest_framework para mejorar la legibilidad de los códigos. Esto facilita entender si la respuesta es correcta sin memorizar números.
El viewset exigía el permiso “doctor” y bloqueaba el acceso anónimo, por lo que el test devolvió 403. Esto evidenció que la combinación is authenticated or read only no funcionaba como se esperaba. La corrección fue requerir autenticación y rol de doctor simultáneamente.
Política original: solo “doctor”, sin lectura pública.
Resultado de prueba: 403, acceso prohibido.
Ajuste: usar autenticación y rol “doctor” juntos.
Reejecución con manage.py test: las pruebas pasan.
Habilidades y conceptos aplicados:
Diseño de pruebas unitarias con TestCase y setup reutilizable.
Creación de datos con ORM para paciente y doctor.
Simulación de requests con ApiClient y contenido JSON.
Resolución de rutas con reverse y soporte de show_urls.
Aserciones con self.assertEqual y status legible.
Gestión de permisos y detección de 403 en viewsets.
Ejecución de pruebas con manage.py test.
¿Te gustaría que profundicemos en más casos de pruebas, como creación, actualización o eliminación de appointments? Comparte tus dudas o escenarios que quieras validar.
También lo noto necesario, seria un buen próximo curso.
Concuerdo x3
Hice esta prueba para verificar que un doctor pueda ser puesto en vacaciones:
classDoctorViewSetTests(TestCase): def setUp(self): self.patient=Patient.objects.create( first_name='Luis', last_name='Martinez', date_of_birth='1990-12-05', contact_number='12312312', email='example@example.com', address='Dirección de prueba', medical_history='Ninguna',) self.doctor=Doctor.objects.create( first_name='Oscar', last_name='Barajas', qualification='Profesional', contact_number='23412341234', email='example2@example.com', address='Medellín', biography='Sin', is_on_vacation=False,) self.client=APIClient() def test_set_on_vacation(self):"""Prueba para poner un doctor en vacaciones""" url =reverse('doctor-set-on-vacation', kwargs={"pk": self.doctor.id}, # Genera la URL para el doctor específico
) # Realizar la solicitud POST para poner al doctor en vacaciones
response = self.client.post(url) # Verificar que la respuesta sea 200OK self.assertEqual(response.status_code, status.HTTP_200_OK) # Refrescar el objeto doctor de la base de datos para verificar el cambio
self.doctor.refresh_from_db() # Verificar que el campo 'is_on_vacation' ahora sea True self.assertTrue(self.doctor.is_on_vacation) # Verificar que la respuesta contiene el mensaje adecuado
self.assertEqual(response.data['status'],"El doctor está en vacaciones")
Genial!
Para realizar pruebas unitarias de **endpoints anidados** usando APIClient en Django REST Framework, puedes simular las solicitudes HTTP a tus endpoints dentro de un contexto de prueba. Aquí te explico cómo hacerlo paso a paso.
### 1. **Escenario: Endpoints Anidados**
Supongamos que tienes un endpoint de **doctores** y uno de **pacientes** anidado bajo los doctores. Un ejemplo de URLs anidadas podría ser algo como:
- /api/doctors/ → Lista de doctores.
- /api/doctors/{doctor\_id}/patients/ → Lista de pacientes para un doctor en particular.
### 2. **Configuración de las Pruebas**
Primero, asegúrate de tener configurado tu APIClient y tus modelos.
#### Modelos
Si tienes los siguientes modelos:
\# models.py
from django.db import models
classDoctor(models.Model):  name = models.CharField(max\_length=100)  specialization = models.CharField(max\_length=100)classPatient(models.Model):  name = models.CharField(max\_length=100)  doctor = models.ForeignKey(Doctor, related\_name='patients', on\_delete=models.CASCADE)
#### Serializers
Los serializers podrían verse así:
\# serializers.py
from rest\_framework import serializers
from.models import Doctor, Patient
classPatientSerializer(serializers.ModelSerializer):  class Meta:  model = Patient  fields = \['id', 'name']classDoctorSerializer(serializers.ModelSerializer):  patients = PatientSerializer(many=True, read\_only=True)  class Meta:  model = Doctor  fields = \['id', 'name', 'specialization', 'patients']
from rest\_framework.routers import DefaultRouter
from.views import DoctorViewSet, PatientViewSet
router = DefaultRouter()router.register(r'doctors', DoctorViewSet)
\# URL anidadas para pacientes de un doctor específico
from rest\_framework\_nested import routers
doctor\_router = routers.NestedSimpleRouter(router,r'doctors', lookup='doctor')doctor\_router.register(r'patients', PatientViewSet, basename='doctor-patients')urlpatterns = router.urls + doctor\_router.urls
### 3. **Prueba de Endpoints Anidados**
Para realizar una prueba unitaria sobre los endpoints anidados usando APIClient, sigue los pasos siguientes:
#### Crear el archivo de pruebas
\# tests.py
from rest\_framework.test import APITestCase, APIClient
from django.urls import reverse
from rest\_framework import status
from.models import Doctor, Patient
classDoctorPatientAPITests(APITestCase):  def setUp(self):  self.client = APIClient()     \# Crear un doctor y pacientes  self.doctor = Doctor.objects.create(name="Dr. Smith", specialization="Cardiology")  self.patient1 = Patient.objects.create(name="John Doe", doctor=self.doctor)  self.patient2 = Patient.objects.create(name="Jane Doe", doctor=self.doctor)     \# URL para obtener la lista de pacientes de un doctor específico  self.patients\_url = reverse('doctor-patients-list', kwargs={'doctor\_pk': self.doctor.id})  def test\_get\_patients\_for\_doctor(self):  """Prueba para obtener la lista de pacientes de un doctor específico"""     \# Simular una solicitud GET al endpoint de pacientes  response = self.client.get(self.patients\_url)     \# Comprobar que la respuesta tenga código 200 (éxito)  self.assertEqual(response.status\_code, status.HTTP\_200\_OK)     \# Verificar que los pacientes se devuelvan correctamente en la respuesta  self.assertEqual(len(response.data), 2)  self.assertEqual(response.data\[0]\['name'], self.patient1.name)  self.assertEqual(response.data\[1]\['name'], self.patient2.name)  def test\_create\_patient\_for\_doctor(self):  """Prueba para crear un paciente bajo un doctor específico"""     \# Datos para el nuevo paciente  data = {  'name': 'Tom Doe'  }     \# Simular una solicitud POST al endpoint de pacientes  response = self.client.post(self.patients\_url, data, format='json')     \# Comprobar que la respuesta tenga código 201 (creado)  self.assertEqual(response.status\_code, status.HTTP\_201\_CREATED)     \# Verificar que el paciente fue creado correctamente y asociado al doctor  self.assertEqual(Patient.objects.filter(doctor=self.doctor).count(), 3)  self.assertEqual(response.data\['name'], 'Tom Doe')
### Explicación de la Prueba
1. **setUp**: Configuramos el cliente de la API (APIClient), creamos un Doctor y dos Patient. También generamos la URL de los pacientes anidados bajo un doctor específico.
2. **test\_get\_patients\_for\_doctor**:
- Enviamos una solicitud GET al endpoint anidado /doctors/{doctor\_id}/patients/.
- Verificamos que la respuesta tenga código de estado 200 OK.
- Comprobamos que se devuelvan los pacientes correctos en la respuesta.
3. **test\_create\_patient\_for\_doctor**:
- Enviamos una solicitud POST para crear un nuevo paciente bajo el doctor.
- Verificamos que el código de estado sea 201 CREATED.
- Comprobamos que el paciente fue correctamente añadido a la base de datos y asociado al doctor.
### 4. **Ejecutar las pruebas**
Para ejecutar las pruebas, utiliza el siguiente comando en la terminal:
python manage.py test
Esto ejecutará las pruebas en tu proyecto y validará que los endpoints anidados funcionen correctamente.
Podemos hacer Test similando el login. Aquí les dejo mis pruebas unitartias para doctor.
Tengo una pregunta que me surgió desde cursos anteriores como Flask, FastAPI y Django. Vengo del mundo de Typescript, y es que hay dos mundos muy diferentes, UnitTests y E2E.
En Typescript o Javascript estas pruebas que se han hecho serían pruebas E2E, en las Api de Python existen pruebas unitarias como las de Javascript? Probar de forma aislada una clase, un método, una función?