Antes del cómo, el cuándo. No todos los cambios necesitan versión:
Cambio
¿Rompe?
Añadir un campo a una respuesta
No
Añadir un punto de acceso
No
Añadir un parámetro opcional
No
Hacer opcional un campo obligatorio
No
Quitar un campo
Sí
Renombrar un campo
Sí
Cambiar el tipo de un campo
Sí
Hacer obligatorio un campo opcional
Sí
Cambiar un código de respuesta
Sí
Cambiar el formato de los errores
Sí
La regla detrás de la tabla: añadir no rompe, quitar y cambiar sí. Un cliente que ignora los campos que no conoce sobrevive a lo primero.
Y hay dos casos que no parecen romper y rompen:
Cambiar el orden por defecto de un listado. Alguien que muestra los cinco primeros verá otros cinco.
Ajustar una validación para que sea más estricta. Datos que antes se aceptaban dejan de aceptarse, y del lado del cliente eso son errores nuevos en código que no ha cambiado.
Fíjate también en que hay un cambio de la tabla que se hace sin querer constantemente: añadir un campo no rompe, y con fields = '__all__' ocurre cada vez que alguien toca el modelo. Ahí el riesgo no es romper sino publicar de más, que es la lección 14.
En la ruta es lo más común y lo que conviene por defecto. Se ve, se copia, se prueba con el navegador y sale en los registros. Su pega teórica es que el mismo recurso tiene dos direcciones, lo que a los puristas de REST les molesta; su ventaja práctica es que todo el mundo lo entiende.
En la cabecera es lo más correcto —la dirección identifica el recurso y la versión es una preferencia— y es lo que usa GitHub. Y es invisible: no se puede probar pegando una dirección en el navegador, no sale en los registros sin configurarlo, y quien consuma la API se olvidará de mandarla y acabará en la versión por defecto sin saberlo.
En un parámetro es lo más fácil de probar y lo que peor envejece, porque se mezcla con los filtros.
En el nombre del servidor solo tiene sentido si además vas a desplegarlas por separado.
La recomendación práctica: en la ruta, salvo que ya tengas una razón para lo contrario.
DRF trae su propio cliente, que es el de Django sabiendo de JSON:
python
from rest_framework.test import APITestCase
classCatalogoTest(APITestCase):@classmethod
defsetUpTestData(cls):cls.ana = User.objects.create_user('ana','a@t.es','clave-larga-123')cls.ed = Editorial.objects.create(nombre='Anagrama', pais='España')cls.autor = Autor.objects.create(nombre='Óscar Núñez')for i inrange(30):
libro = Libro.objects.create(titulo=f'Libro {i}', isbn=f'978-{i:05d}',
precio='10.00', editorial=cls.ed)
libro.autores.add(cls.autor)defsetUp(self):self.client.force_authenticate(self.ana)
Cuatro cosas de esas líneas:
force_authenticate salta la autenticación entera. No comprueba tokens ni contraseñas: pone el usuario. Es lo que quieres en las pruebas que no van de autenticación, y significa que hay que probar la autenticación aparte.
setUpTestData crea los datos una vez para toda la clase, no una por prueba, que es la lección 39 del módulo 4.
format='json' en cada petición con cuerpo. Sin él, el cliente manda un formulario, y eso cambia cómo se interpretan las listas y los valores nulos, de modo que la prueba pasa por un camino distinto al del cliente real.
El autor tiene tildes a propósito. Es la diferencia entre unas pruebas que detectan el problema de la lección 19 y unas que no.
test_anonimo_no_entra ................................ ok
test_buscar_con_tilde ................................ ok
test_buscar_en_minuscula ............................. ok
test_buscar_sin_tilde ................................ FAIL
test_crear ........................................... ok
test_crear_sin_datos_da_400_por_campo ................ ok
test_el_listado_no_crece_con_las_filas ............... ok
test_el_precio_viaja_como_texto ...................... ok
test_isbn_repetido ................................... ok
test_listar .......................................... ok
test_patch_con_errata_no_cambia_nada ................. FAIL
Las dos que fallan son las importantes, y fallan a propósito:
Salida en pantalla
FAIL: test_buscar_sin_tilde
AssertionError: 0 != 30 : la búsqueda no encuentra sin tilde
FAIL: test_patch_con_errata_no_cambia_nada
AssertionError: 'Libro 0' != 'NUEVO' : el PATCH con errata contesta 200 sin guardar
Son los dos fallos silenciosos de las lecciones 19 y 13, y ninguno de los dos lo detecta una prueba normal, porque los dos devuelven 200.
Y hay un matiz que se ve comparando las tres pruebas de búsqueda: Núñez funciona, núñez en minúscula también, y Nunez sin tildes no. La segunda pasa porque la única letra que hay que convertir a minúscula es la N, que es ASCII. Con Óscar no pasaría, porque habría que convertir la Ó. Es el mismo problema asomando de dos maneras según la palabra, que es lo que lo hace difícil de diagnosticar.