Capítulo 05 de 10 · 10 min de lectura
De criterios a tests
Cuántos tests necesita un criterio, cuáles son los primeros que se quedan fuera y cómo ver, sin leer el código, qué parte del contrato no vigila nadie.
Un criterio, al menos un test
El capítulo anterior terminaba con once tests para cinco criterios. Falta explicar de dónde sale ese número, y cómo se sabe que no falta ninguno.
La regla de partida está en el AGENTS.md de acorta:
TDD: tests primero, uno al menos por criterio de la spec (el test nombra el identificador:
URL-04). Ejecútalos y confirma que fallan antes de implementar. Después, lo mínimo para el verde. Refactor al final.
Tiene dos partes y la segunda es la que se olvida. «Uno al menos por criterio» dice cuánto hay que probar. «El test nombra el identificador» es lo que permite comprobarlo: si cada test dice a qué criterio responde, la pregunta «¿qué parte de la spec no tiene test?» la contesta un programa, sin que nadie lea el código.
¿Cuántos tests por criterio?
Los que el criterio pida. No hay cuota. Este es ALI-02, de la spec de acorta:
- **ALI-02** — Dado un alias de menos de 3 o de más de 32 caracteres, entonces error
`alias: debe tener entre 3 y 32 caracteres`. Con 3 y con 32 se acepta. Se cuentan
caracteres, no bytes: `ñu` tiene 2 y da este error; `ñus` tiene 3 y da el de ALI-03.
Y estos son los tests que lo nombran, tal como los imprime go test:
TestValidateAlias/ALI-02_2_caracteres
TestValidateAlias/ALI-02_3_caracteres_se_acepta
TestValidateAlias/ALI-02_32_caracteres_se_acepta
TestValidateAlias/ALI-02_33_caracteres
TestValidateAlias/ALI-02_se_cuentan_caracteres,_no_bytes:_ñu_son_2
TestValidateAlias/ALI-02_ñus_son_3_caracteres:_pasa_la_longitud_y_falla_ALI-03
TestValidateAlias/ALI-02_17_ñ_son_34_bytes_pero_17_caracteres:_falla_ALI-03,_no_la_longitud
TestValidateAlias/ALI-02_33_ñ_son_33_caracteres
Ocho tests para una frase. Salen de tres sitios:
- Cada límite da dos tests, uno a cada lado. El criterio dice «menos de 3» y «más de 32»: se prueba con 2 y con 3, con 32 y con 33. Un fallo de uno,
<donde iba<=, solo se ve justo en la frontera. - Cada ejemplo que el criterio nombra es un test.
ñuyñusestán en la spec; pasan al test tal cual. Por eso conviene que la spec traiga ejemplos: son tests ya decididos. - Lo que separa este criterio del vecino. Diecisiete
ñson 34 bytes y 17 caracteres. Si el código contara bytes daría el error de longitud; contando caracteres da el deALI-03. Ese test distingue las dos lecturas.
El encargo que recibió el agente lo pedía así:
Incluye los casos límite que la spec nombra (2048 bytes exactos, alias de 3 y de 32, caducidad igual al instante actual).
En acorta hay criterios con un solo test y uno con trece: API-06, el del cuerpo de una petición que no es lo que se espera. Hay muchas formas de mandar algo que no vale, y cada una es un caso.
Los casos negativos
Un caso negativo comprueba que algo no se acepta o que algo no ocurre. En la tabla de tests del alias hay 22 casos, y 14 esperan un error. Probar una validación consiste sobre todo en probar lo que rechaza.
Hay un segundo tipo, más fácil de olvidar: lo que no debe pasar además. Dos criterios de acorta lo dicen con cuatro palabras al final:
ALI-06: un alias ya usado da un error, «y no guarda nada».RED-04: una peticiónHEADresponde igual que unGET, «sin cuerpo y sin sumar visita».
El error es fácil de comprobar. Lo otro pide mirar lo que no ha cambiado: que la lista de enlaces sigue igual, que el contador de visitas no se ha movido. Un test que solo mira la respuesta deja pasar un programa que contesta bien y guarda basura.
Y son los primeros que se quedan fuera, también de la spec. La de acorta se aprobó con 97 criterios, después de pasar por el agente que la redactó y por una revisión humana. Hoy tiene 100. Los tres que se añadieron:
| Criterio | Qué dice |
|---|---|
ALM-09 |
Abrir la base de datos en una ruta que no se puede crear devuelve un error y no deja nada abierto |
RES-05 |
Si no se puede sumar la visita, no se devuelve la URL |
RED-07 |
Si el servicio falla por dentro, la respuesta es un 500 sin detalles |
Los tres hablan de lo que pasa cuando algo va mal. Ninguno estaba en la spec, y los tres salieron de una pregunta que hizo un agente al escribir los tests. La revisión de una de esas fases lo resume así en la bitácora:
La spec solo describía los caminos limpios.
La matriz
Con cada test nombrando su criterio, se puede cruzar la spec con los tests. Esta es la matriz de acorta entera: sus 100 criterios, y en cada casilla cuántos de sus 284 tests lo nombran.
- 100criterios
- 0sin ningún test
- 47con un solo test
- 28tests sin criterio
0 ningún test lo nombra 1 depende de un solo test2 dos o más
Los 100 criterios de acorta tienen al menos un test que los nombra. Pulsa un criterio para ver cuáles.
ALI-028 tests · spec 002
Dado un alias de menos de 3 o de más de 32 caracteres, entonces error alias: debe tener entre 3 y 32 caracteres. Con 3 y con 32 se acepta. Se cuentan caracteres, no bytes: ñu tiene 2 y da este error; ñus tiene 3 y da el de ALI-03.
- linkTestValidateAlias/ALI-02_17_ñ_son_34_bytes_pero_17_caracteres:_falla_ALI-03,_no_la_longitud
- linkTestValidateAlias/ALI-02_2_caracteres
- linkTestValidateAlias/ALI-02_3_caracteres_se_acepta
- linkTestValidateAlias/ALI-02_32_caracteres_se_acepta
- linkTestValidateAlias/ALI-02_33_caracteres
- linkTestValidateAlias/ALI-02_33_ñ_son_33_caracteres
- linkTestValidateAlias/ALI-02_ñus_son_3_caracteres:_pasa_la_longitud_y_falla_ALI-03
- linkTestValidateAlias/ALI-02_se_cuentan_caracteres,_no_bytes:_ñu_son_2
Quita tests y mira la casilla: con el último que quites, el criterio se queda sin nadie que lo compruebe.
Los 28 tests que no nombran ningún criterio
- linkTestErrorMessages/errores_centinela
- linkTestErrorMessages/ValidationError_con_un_mensaje
- linkTestErrorMessages/ValidationError_con_varios_mensajes_los_une_con_punto_y_coma
- linkTestErrorMessages/ValidationError_se_recupera_con_errors.As
- linkTestValidateAllErrorsAtOnce/entrada_correcta:_sin_mensajes
- linkTestValidateAllErrorsAtOnce/los_tres_campos_fallan
- linkTestValidateAllErrorsAtOnce/solo_alias_y_expires_at,_saltándose_url
- linkTestValidateAllErrorsAtOnce/solo_url_y_expires_at
- linkTestValidateAllErrorsAtOnce/un_alias_con_varios_fallos_da_solo_el_primero_(caracteres_antes_que_guion)
- linkTestValidateAllErrorsAtOnce/un_alias_con_varios_fallos_da_solo_el_primero_(longitud)
- linkTestValidateAllErrorsAtOnce/una_url_con_varios_fallos_da_solo_el_primero_(esquema_antes_que_dominio)
- linkTestValidateEmptyValidOnErrors/fallan_los_tres
- linkTestValidateEmptyValidOnErrors/solo_falla_el_alias
- linkTestValidateEmptyValidOnErrors/solo_falla_expires_at
- linkTestValidateEmptyValidOnErrors/solo_falla_la_url
- linkTestValidateExpiresAtClockWithFraction
- linkTestValidateReturnsCleanValid
- shortenerTestCreate_CreatedAtEsElRelojEnUTCSinFracciones
- shortenerTestCreate_EntradaInvalidaDaValidationErrorYNoGuardaNada/caducidad_pasada
- shortenerTestCreate_EntradaInvalidaDaValidationErrorYNoGuardaNada/tres_errores_a_la_vez
- shortenerTestCreate_EntradaInvalidaDaValidationErrorYNoGuardaNada/un_error
- shortenerTestCreate_GuardaLaCaducidadValidada
- shortenerTestFalloDelAlmacenSePropagaSinTraducirse/Create_con_alias
- shortenerTestFalloDelAlmacenSePropagaSinTraducirse/Create:_un_error_de_Insert_que_no_es_ErrCodeTaken_no_se_reintenta
- shortenerTestFalloDelAlmacenSePropagaSinTraducirse/Delete
- shortenerTestFalloDelAlmacenSePropagaSinTraducirse/List
- shortenerTestFalloDelAlmacenSePropagaSinTraducirse/Resolve
- cmd/acortaTestCLI_UnknownFlagIsUsageError
Datos de acorta en el commit 15125e2: los nombres que imprimen go test y Vitest.
Tres cosas que mirar:
- Ningún criterio está a cero. Es el mínimo que pide la regla, y se comprueba en un segundo.
- 47 criterios dependen de un solo test, los de borde discontinuo. Pulsa la tercera casilla de la fila
RES, que esRES-03, y quita su test. La casilla se pone en rojo. Es lo que pasa cuando alguien borra un test que molesta, y el capítulo sobre revisión volverá a ello: si era el único, la matriz lo ve. Si había más, no. - 28 tests no nombran ningún criterio. Abre la lista. Comprueban cosas que la spec dice en un párrafo, sin número: en qué orden se devuelven los errores, cómo se juntan varios mensajes. La matriz leída al revés señala reglas que existen y no tienen identificador. Un test sin criterio no sobra, pero pregunta dónde está escrito lo que comprueba.
Cincuenta y dos huecos que no lo eran
Pulsa ahora «Tal cual está en la spec». La matriz se llena de rojo: 52 criterios sin test.
Los tienen. Lo que cambia es cómo se busca. Las piezas de acorta las escribieron agentes distintos, y la regla «el test nombra el identificador» la cumplió cada uno a su manera:
| Dónde | Cómo se llama el test |
|---|---|
link |
TestValidateAlias/ALI-02_2_caracteres |
store, shortener, cmd/acorta |
TestALM03_CodigoRepetido |
server |
TestAPI06_CuerpoInvalido/API-06_array |
web |
WEB-01: envía alias si se ha escrito |
En Go, el nombre de una función no admite guiones. El agente del dominio metió el identificador en el nombre de cada caso de la tabla, donde sí caben. Los otros lo pusieron en el nombre de la función, sin guion, y con guion en un comentario encima o en el mensaje de fallo. Buscando ALM-03 en el código aparece. En la salida de los tests, que es lo que lee un informe automático, no: ahí solo está TestALM03.
Se vio a mitad del proyecto. El agente de los casos de uso lo resolvió con comentarios y lo contó en su informe:
He añadido encima de cada una de las 27 funciones un comentario que empieza por su criterio con guion (
// BOR-01: …). No he cambiado ningún nombre de función.
Y a partir de ahí los encargos lo pedían:
Cada test nuevo lleva su identificador con guion (
RED-07).
La lección es de redacción de reglas. «Que lo nombre» dejaba abierto dónde y con qué forma, y esa forma es justo lo que va a buscar el programa que calcule la matriz. La regla de la plantilla lo dice ahora entero: el identificador escrito tal cual, en el nombre del test, y si el lenguaje no admite guiones, en un comentario justo encima.
Pídela, y luego compruébala
Todos los encargos del paso rojo de acorta terminaban pidiendo lo mismo en el informe:
una tabla criterio → test
Sirve para que el agente repase su propio trabajo antes de entregarlo. No sirve como prueba. El agente de los casos de uso corrigió su propio recuento en el informe siguiente:
Dije 27 tests y eran 26. Ahora hay 27 con el nuevo de RES-05.
Por eso la tabla del informe se lee, y la matriz se calcula aparte, con los nombres que imprimen los tests. Es una comprobación barata y no depende de lo que cuente nadie.
Lo que la matriz no dice
Dice si cada criterio tiene quien lo vigile. No dice si vigila bien.
Un test puede llamarse ALI-02 y no comprobar nada de ALI-02. Un criterio con cinco tests puede estar peor cubierto que otro con uno bueno. Y la matriz solo conoce los criterios escritos: lo que le falte a la spec le falta también a ella.
Para lo primero está la revisión, que tiene su capítulo. Para lo segundo, una técnica que rompe el código a propósito y mira si algún test se entera; es el último.
Lo que viene
Las reglas de estos capítulos (la spec primero, los tests antes que el código, el identificador en cada test) no sirven de nada si hay que repetírselas al agente en cada encargo. Viven en un fichero que lee él solo. El capítulo siguiente trata de ese fichero: qué poner, qué dejar fuera y qué cambia cuando falta una línea.