# Los tests son cómo cambias el código sin miedo

Curso exprés sobre testing. 19 conceptos — Unit Test, Integration Test, E2E, Test Pyramid, Stub y Mock, TDD, Code Coverage, Flaky Test, CI Gate y más — cada uno con su nombre en inglés, un diagrama, una tarjeta y un ejemplo. La idea central: el sentido de los tests no es probar la corrección una vez, es la libertad de cambiar el código mañana sin romper lo que hoy funciona. Al final, una chuleta con todos los términos.

- **Author:** Fedor Chishchin
- **Level:** intro
- **Reading time:** ~35 minutos de lectura
- **Published:** 2026-06-13
- **Source:** https://fedorthinks.com/es/learn/testing-express

---

## 1. Por qué testeamos

Casi todo el mundo malinterpreta para qué sirven los tests. Parecen una forma de probar que el código funciona; su verdadero valor es algo más profundo y más práctico — y verlo cambia cómo los escribes.

### El mapa: 19 conceptos en siete capítulos

> Un mapa de metro: primero ves todas las líneas de golpe, luego recorres estación por estación.

Cada concepto del curso está en uno de siete capítulos. Primero, por qué existen los tests. Luego, los tipos de test y cómo equilibrarlos. Luego, los falsos que necesitas para aislar un trozo de código. Después: el oficio de un buen test, confiar en el conjunto de tests y ponerlo todo a trabajar.

_Diagram: Siete capítulos, 19 conceptos. Al final te espera una chuleta con todos ellos._

### Cómo leer este curso

> Un pasaporte: los mismos campos para todos, así es fácil compararlos.

Cada concepto recibe el mismo trato: su **nombre en inglés** (recuérdalo — es el término que encontrarás en la documentación y en las entrevistas), una imagen de la vida real, un **diagrama** y una **tarjeta** con campos: _problema_, _idea_, _ejemplo_, _coste_, _cuándo usarlo_ y conceptos _relacionados_. Una idea recorre todo el curso: **los tests son un seguro para los cambios futuros**, no un certificado de que el código funcionó una vez.

### Automated Test — código que comprueba código

> Una segunda persona que rehace tus cuentas en una calculadora y marca las que no cuadran — automática, incansable e idéntica cada vez.

Un **test** es un trozo pequeño de código que ejecuta tu código con una entrada conocida y comprueba que el resultado es el que esperas. «Dado 2 y 3, ¿`add` devuelve 5?» Si sí, el test **passes**; si no, **fails** y te lo dice. Esa es toda la mecánica. Lo potente es que es automático y repetible — puedes ejecutar miles de estas comprobaciones en segundos, cada vez que cambias algo, sin que un humano vuelva a verificar a mano.

_Diagram: Un test da al código una entrada conocida y compara el resultado con lo esperado: coincide — pasa, no coincide — falla._

#### Automated Test (test, autotest)

- **Problema:** Comprobar a mano es lento y aburrido — así que tras cada cambio nadie vuelve a comprobarlo todo.
- **Idea:** Escribe la comprobación como código: entrada conocida → llamada → comparar con lo esperado. Resultado: pasa o falla.
- **Ejemplo:** «`add(2, 3)` debe devolver 5» — una línea, se ejecuta en milisegundos.
- **Coste:** Los tests también son código: hay que escribirlos, leerlos y mantenerlos.
- **Cuándo usarlo:** Para cualquier código que vayas a seguir cambiando.
- **Relaciones:** Caza **Regressions**; viene como **Unit**, **Integration** y **E2E**; se ejecuta en el **CI Gate**.

### Safety Net — lo importante es cambiar sin miedo

> El arnés de seguridad de un escalador: no hace la escalada por ti, pero te deja intentar un movimiento audaz porque un resbalón no será fatal. Escalas con más fuerza porque, si caes, te sujeta.

Aquí está el malentendido. Los tests no van sobre todo de probar que el código de hoy es correcto — van sobre **mañana**. Cuando cambias código, lo refactorizas o añades una función, un buen conjunto de tests (**test suite**) te dice al instante si rompiste algo que antes funcionaba. Esa **red de seguridad** es lo que te deja cambiar una base de código grande con confianza en lugar de tener miedo de tocar nada. Los tests son cómo sigues yendo rápido sin que las cosas se rompan en silencio a tu espalda.

_Diagram: Todo cambio pasa por el conjunto de tests: verde — lo publicas con confianza; rojo — lo arreglas ahora, no cuando se queje un usuario._

### Regression — cuando un cambio rompe lo que funcionaba

> Una hilera de hilos trampa por toda una casa: en el momento en que algo perturba una habitación que ni siquiera mirabas, salta una alarma — no tienes que patrullar cada habitación tú mismo.

Una **regression** es cuando un cambio rompe algo que antes funcionaba — a menudo en un sitio lejano a lo que tocaste. Sin tests, las regressions se esconden hasta que un usuario las encuentra. Con tests, en el momento en que tu cambio rompe un comportamiento viejo, un test se pone en rojo y lo señala, justo ahí. Esta es la recompensa del día a día: haces un cambio, ejecutas los tests y sabes al instante si también rompiste algo en otra parte. El conjunto vigila toda la casa mientras tú trabajas en una habitación.

_Diagram: Cambiaste el carrito y se rompieron las facturas — a tres módulos de distancia. El test de facturas se pone rojo enseguida._

#### Regression (regression bug)

- **Qué es:** Un cambio rompe un comportamiento que antes funcionaba.
- **Trampa:** A menudo se rompe lejos de donde editaste, en una parte distante del sistema.
- **Ejemplo:** Cambiaste el total del carrito — y las facturas empezaron a mostrar un importe incorrecto.
- **Cómo cazarla:** Un conjunto de tests sobre el comportamiento antiguo, ejecutado en cada cambio.
- **Consejo:** ¿Encontraste un bug? Añade un test para que nunca vuelva.
- **Relaciones:** La caza un **Automated Test**; la vigila automáticamente el **CI Gate**.

> **La pregunta de enfoque:** «¿Escribo este test para probar que el código funciona una vez — o para cazar el día en que alguien cambie algo y rompa esto sin darse cuenta?»

> Un test comprueba el código automáticamente. Su verdadero valor no es una prueba puntual — es la libertad de cambiar mañana sin romper en silencio lo que funciona hoy.

## 2. Unit tests

El cimiento del testing es el unit test: una comprobación rápida y enfocada de una pieza pequeña de código por sí sola. La mayoría de tus tests serán estos, y con razón.

### Unit Test — una pieza pequeña en aislamiento

> Comprobar que una sola pieza de Lego tiene la forma y el tamaño correctos antes de construir con ella — no montar el castillo entero para descubrir que una pieza estaba mal.

Un **unit test** comprueba una **unidad** pequeña — normalmente una sola función — en **aislamiento** del resto del sistema. Le das una entrada, compruebas su salida, nada más involucrado. Como la pieza es pequeña e independiente, cuando el test falla sabes exactamente qué se rompió y dónde. Los unit tests son el microscopio del testing: estrechos, precisos y apuntados a una sola cosa a la vez.

_Diagram: Un unit test: entrada → una función → comprobar la salida. La base de datos, la red y el disco se quedan fuera._

#### Unit Test (test de módulo)

- **Problema:** Cuando falla una comprobación grande, no sabes cuál de cien partes tiene la culpa.
- **Idea:** Comprueba una función cada vez, aislada de la base de datos, la red y todo lo demás.
- **Ejemplo:** `calculateTax(100)` devuelve `20` — sin base de datos, sin HTTP, en milisegundos.
- **Coste:** No ve cómo funcionan juntas las piezas — de eso se encargan otros tests.
- **Cuándo usarlo:** Para la mayoría de tus tests: lógica de negocio, cálculos, reglas, casos límite.
- **Relaciones:** La base de la **Test Pyramid**; se aísla con **Test Doubles**; tiene la forma **Arrange–Act–Assert**.

### Rápidos y muchos — esa es toda la idea

> Un corrector ortográfico que escanea el documento entero en un parpadeo — tan rápido que lo ejecutas constantemente, no una revisión lenta que haces una vez al mes.

Como cada unit test es diminuto y no toca nada externo, corre en **milisegundos** — así que puedes tener miles y ejecutarlos todos en cada cambio, constantemente. Esa velocidad es el punto: un conjunto de tests que puedes ejecutar en segundos se ejecuta todo el tiempo, cazando roturas en el instante en que ocurren. Un conjunto lento se salta, y un conjunto que se salta no protege nada. Rápidos y abundantes es lo que hace de los unit tests el caballo de batalla.

_Diagram: Cuanto más bajo el nivel, más rápido el test. Un conjunto rápido se ejecuta en cada edición — por eso caza las roturas._

### Arrange–Act–Assert — preparar, actuar, comprobar

> Un experimento sencillo: preparas las condiciones, haces la única acción y luego compruebas el resultado. Preparación clara, un movimiento, una verificación.

Un buen unit test tiene tres pasos simples — el patrón **Arrange–Act–Assert (AAA)**: prepara las entradas, llama una sola vez a lo que pruebas y luego afirma (assert) que el resultado es el esperado. Una **assertion** es la comprobación en sí — «la respuesta debe ser igual a 5.» Mantener los tests en esta forma limpia los hace legibles y hace evidente un fallo. Un test difícil de leer es uno en el que nadie confiará ni querrá mantener.

_Diagram: Los tres pasos de un test: Arrange arma el carrito, Act calcula el total, Assert lo compara con lo esperado._

#### Arrange–Act–Assert (AAA)

- **Problema:** Un test enredado: preparación, llamadas y comprobaciones mezcladas — no se ve qué verifica.
- **Idea:** Tres bloques en orden: preparación → una acción → comprobación (assertion).
- **Ejemplo:** Arrange: un carrito con dos artículos. Act: `cart.total()`. Assert: el total es igual a 20.
- **Coste:** Casi ninguno — solo la disciplina de una acción por test.
- **Cuándo usarlo:** Siempre, en todo test — unit, integration o end-to-end.
- **Relaciones:** Hace legible un test — y por tanto **Living Documentation**; la columna vertebral de un **Unit Test**.

> **La pregunta del unit test:** «¿Puedo probar esta pieza por sí sola, rápido, con un arrange-act-assert claro — para que, cuando falle, sepa exactamente qué se rompió?»

> Un unit test comprueba una pieza pequeña en aislamiento, en milisegundos — así ejecutas miles constantemente. Prepara la entrada, actúa una vez, afirma el resultado.

## 3. Integration, e2e y la pirámide

Los unit tests comprueban las piezas por separado, pero el software son piezas trabajando juntas. Dos tipos de test más amplios cubren eso — y el equilibrio justo entre los tres tiene una forma que merece conocerse.

### Integration Test — piezas trabajando juntas

> Después de comprobar cada pieza de Lego, encajas unas cuantas para asegurarte de que de verdad se conectan — las piezas estaban bien por separado, pero ¿encajan?

Un **integration test** comprueba que varias partes funcionan correctamente _juntas_ — tu código hablando con la base de datos, dos servicios intercambiando datos. Cada unidad puede ser perfecta por separado y aun así no conectar: un formato que no coincide, una suposición equivocada sobre el otro lado. Los integration tests cazan justo esas **costuras** (seams). Son más lentos que los unit tests porque intervienen más piezas reales en movimiento, pero verifican las uniones que los unit tests, por diseño, se saltan.

_Diagram: Un integration test comprueba la costura: la consulta a la base de datos y su respuesta, el JSON entre servicios. Los bugs se esconden en las uniones._

#### Integration Test (comprobación de integración)

- **Problema:** Cada parte pasa sus unit tests, pero juntas no funcionan.
- **Idea:** Ejecuta varias partes reales juntas y comprueba la unión entre ellas.
- **Ejemplo:** Un repositorio escribe un usuario en una base de datos de prueba real y lo lee de vuelta.
- **Coste:** Más lento que los unit tests; necesita un entorno montado (base de datos, servicios).
- **Cuándo usarlo:** Para las costuras: código ↔ base de datos, servicio ↔ servicio, tu código ↔ una API de terceros.
- **Relaciones:** La capa intermedia de la **Test Pyramid**; caza lo que oculta el **Over-mocking**.

### End-to-End Test — todo el sistema, como un usuario

> Un ensayo general de toda la obra, de principio a fin, en el escenario real — no solo las frases o el atrezo, sino la función entera tal como la verá el público.

Un **end-to-end test** (**E2E**) maneja todo el sistema como lo haría un usuario real — pulsa el botón, rellena el formulario, comprueba que pasó lo correcto en toda la pila. Es el test más realista y la confirmación más valiosa de que el producto realmente funciona. Pero también es el más lento y el más **frágil**: muchas partes deben estar en marcha a la vez, y un pequeño cambio de interfaz puede romperlo. Usas los e2e tests para unos pocos recorridos críticos, no para todo.

_Diagram: Un test E2E va del usuario por la interfaz, la API y la base de datos — y comprueba el resultado: ¿se hizo el pedido?_

#### End-to-End Test (E2E)

- **Problema:** Cada parte está probada, pero nadie sabe si el producto funciona como un todo.
- **Idea:** Recorre el camino de un usuario por toda la pila real y comprueba el resultado.
- **Ejemplo:** Un robot de navegador: inicia sesión, añade un artículo al carrito, paga, ve «pedido realizado».
- **Coste:** El más lento y el más frágil; se rompe con pequeños cambios de interfaz.
- **Cuándo usarlo:** Para pocos recorridos críticos: inicio de sesión, pago, el flujo principal.
- **Relaciones:** La punta de la **Test Pyramid**; con demasiados te sale el cono de helado.

### Test Pyramid — el equilibrio justo

> Una pirámide se sostiene porque su base es ancha y su punta estrecha — ponla del revés sobre la punta y se cae. La forma misma es la estabilidad.

La **test pyramid** es la regla práctica del equilibrio: **muchos** unit tests rápidos en la base, **menos** integration tests en el medio y **un puñado** de end-to-end tests lentos arriba. Esta forma te da una cobertura amplia y rápida a bajo coste, con justo los checks realistas necesarios en la cima. El antipatrón — muchos e2e tests lentos y pocos unit tests — es el **cono de helado** (ice-cream cone), y es lento, inestable y doloroso. Apunta a la pirámide, no al cono.

_Diagram: A la izquierda, la pirámide: muchos unit tests, menos integration, unos pocos e2e. A la derecha, el cono de helado — la misma pirámide al revés._

#### Test Pyramid (pirámide de testing)

- **Problema:** Hay muchos tests, pero el conjunto es lento e inestable — así que la gente deja de ejecutarlo.
- **Idea:** Muchos tests baratos y rápidos abajo, pocos caros y lentos arriba.
- **Ejemplo:** Miles de unit tests, cientos de integration tests, una docena de e2e para los flujos principales.
- **Coste:** Hay que decidir a conciencia qué nivel comprueba cada cosa.
- **Antipatrón:** **Ice-cream cone** — la pirámide al revés: muchos e2e, pocos unit.
- **Relaciones:** Las capas son **Unit**, **Integration**, **E2E**; los e2e de más engendran **Flaky Tests**.

> **La pregunta de la pirámide:** «¿Tengo muchos unit tests rápidos en la base, algunos integration tests para las costuras y solo unos pocos end-to-end tests para los recorridos críticos — o tengo la pirámide al revés?»

> Los unit tests comprueban las piezas por separado, los integration tests las comprueban juntas, los e2e tests comprueban todo el sistema como un usuario. Mantén la pirámide: muchos rápidos, pocos lentos.

## 4. Falsos: stubs y mocks

Para probar una pieza en aislamiento, a menudo tienes que sustituir las cosas reales de las que depende. Eso son los mocks y los stubs — y son tan fáciles de usar mal como útiles.

### Test Double — un falso en lugar de la dependencia real

> Un simulador de vuelo en vez de un avión real: instrumentos y vistas falsos te dejan practicar la habilidad con seguridad, sin el coste ni el riesgo de volar de verdad.

Para probar una pieza en aislamiento, sustituyes sus **dependencias** reales — una base de datos, un servicio de pagos, un envío de correo — por **falsos** que hacen su papel. El nombre general de cualquiera de esos falsos es **test double**, como un doble de acción en una película. Esto te permite probar tu código sin la cosa real, que es lenta, cara o impredecible. No quieres que un unit test cobre una tarjeta de verdad o envíe un correo; metes un falso que finge hacerlo, y así el test sigue siendo rápido, fiable y autocontenido.

_Diagram: El código bajo prueba habla con falsos, no con el Postgres, el Stripe y el servidor de correo reales — esos son lentos, cuestan dinero o envían cosas de verdad._

#### Test Double (falso)

- **Problema:** Las dependencias reales son lentas, cuestan dinero o se comportan de forma impredecible — los tests que las usan son frágiles.
- **Idea:** Sustituye por un objeto que se comporta como la dependencia, pero solo finge.
- **Ejemplo:** En lugar de Stripe, un falso que siempre responde «pago realizado».
- **Coste:** Un falso puede mentir: la cosa real puede comportarse de otra manera.
- **Cuándo usarlo:** Para cosas externas, lentas o aleatorias: red, pagos, correo, hora.
- **Relaciones:** Dos tipos principales — **Stub** y **Mock**; da aislamiento a un **Unit Test**; si te pasas, es **Over-mocking**.

### Stub vs Mock — respuesta enlatada vs llamada comprobada

> Un stub es un recortable de cartón que dice una frase cuando le toca; un mock es un actor que además te informa exactamente de lo que le dijiste y cómo.

Se confunden dos tipos de falso. Un **stub** simplemente devuelve una respuesta fija, enlatada — «finge que la base de datos devuelve este usuario» — para que pruebes cómo maneja tu código esa entrada. Un **mock** también _verifica la interacción_ — «comprueba que mi código llamó a `sendEmail` exactamente una vez, con esta dirección.» Un stub alimenta a tu código; un mock vigila cómo se comporta tu código hacia él. Usa un stub para suministrar datos, un mock para afirmar que ocurrió un efecto secundario.

_Diagram: Un stub suministra datos, y el test comprueba la salida de tu código. Un mock registra la llamada, y el test comprueba la propia llamada._

#### Stub (respuesta enlatada)

- **Qué es:** Un falso que devuelve una respuesta preparada.
- **Ejemplo:** `getUser()` siempre devuelve `{ name: 'Ann' }`.
- **Qué compruebas:** La salida de tu código con esos datos.
- **Cuándo usarlo:** Cuando tu código necesita _recibir_ algo de una dependencia.

#### Mock (registrador de llamadas)

- **Qué es:** Un falso que registra las llamadas y te deja verificarlas.
- **Ejemplo:** Comprobar que `sendEmail` se llamó exactamente una vez, con la dirección correcta.
- **Qué compruebas:** La propia llamada: si ocurrió, cuántas veces, con qué argumentos.
- **Cuándo usarlo:** Cuando importa el efecto secundario: el correo salió, el pago se envió.

### Over-mocking — tests que mienten

> Ensayar una obra con recortables de cartón en lugar de todos los demás actores — puedes clavar tus frases y aun así no tener ni idea de si el reparto real funcionará junto.

Los falsos son potentes pero peligrosos en exceso. Si haces mock de _todo_, tu test pasa contra tus suposiciones sobre cómo se comportan las otras partes — no contra cómo se comportan de verdad. El test se pone en verde mientras la integración real está rota. Ese es el antipatrón del **over-mocking**. Así que haz mock de las dependencias lentas, externas o impredecibles, pero no falsees justo lo que intentas verificar. Un test construido solo con mocks puede pasar mientras el sistema real falla — el verde más peligroso.

_Diagram: En el test, todo es mock — y está en verde. En producción, la base de datos y la API reales — y todo falla._

#### Over-mocking (demasiados mocks)

- **Síntoma:** El test está en verde, pero la misma función falla en producción.
- **Causa:** Todo lo que la rodea es falso — el test solo comprueba tus suposiciones sobre otras partes.
- **Ejemplo:** El mock de la base de datos siempre dice «ok», mientras la base de datos real rechaza la consulta por un campo equivocado.
- **Remedio:** Haz mock solo de lo externo y lento; comprueba las costuras con un **Integration Test**.
- **Relaciones:** Mal uso de los **Test Doubles**; erosiona la confianza igual que un **Flaky Test**.

> **La pregunta de los mocks:** «¿Estoy falseando una dependencia genuinamente externa para ser rápido y aislado — o he hecho tanto mock que el test solo comprueba mis propias suposiciones?»

> Los mocks y los stubs sustituyen a las dependencias reales para que puedas probar en aislamiento. Un stub suministra datos; un mock verifica una llamada. Si abusas del mock, el test pasa mientras la realidad se rompe.

## 5. TDD y buenos tests

Más allá de los tipos de test está el oficio de escribir buenos — y una disciplina, el desarrollo guiado por tests, que invierte el orden habitual y cambia cómo diseñas el código.

### TDD — escribe primero el test

> Dibujar la diana antes de disparar, no después — así apuntas a un objetivo definido en lugar de pintar la diana alrededor de donde cayó la flecha.

El **desarrollo guiado por tests** (**TDD**, test-driven development) invierte el orden habitual: escribes el test _antes_ que el código. El ciclo es **rojo, verde, refactor** — escribe un test que falla para lo que quieres (rojo), escribe el código justo para que pase (verde), luego limpia el código con el test vigilándote (refactor). Escribir primero el test te obliga a definir exactamente qué significa el éxito antes de construir, y garantiza que el código sea testeable, porque nació de un test.

_Diagram: El ciclo TDD: rojo (el test falla) → verde (el mínimo código que pasa) → refactor (limpiar, los tests siguen en verde) → otra vez._

#### Test-Driven Development (TDD, red-green-refactor)

- **Problema:** Los tests se escriben «más tarde» — y el código sale difícil de testear.
- **Idea:** Primero un test que falla, luego el mínimo código, luego una limpieza. Y vuelta a empezar.
- **Ejemplo:** El test «carrito vacío → el total es 0» falla; escribes `total()`; se pone en verde; ordenas.
- **Coste:** Poco familiar y más lento al principio; difícil cuando la propia tarea aún no está clara.
- **Cuándo usarlo:** Para lógica con reglas claras: cálculos, validación, corrección de bugs.
- **Relaciones:** Da código testeable y **Living Documentation**; el refactor se apoya en **Test Behaviour**.

### Test Behaviour, Not Implementation — comprueba qué, no cómo

> Juzgar a un chef por si el plato sabe bien, no por con qué mano sostuvo el cuchillo — te importa el resultado, no los detalles privados de cómo llegó a él.

El hábito más importante para tests duraderos: comprueba **qué** hace el código, no **cómo** lo hace — su **comportamiento**, no su **implementación**. Prueba que `getDiscount` devuelve el precio correcto, no que llamó a tres ayudantes internos concretos en orden. Los tests atados al resultado sobreviven al refactor — puedes reescribir las tripas y siguen pasando. Los tests atados a la implementación se rompen cada vez que ordenas el código, lo que enseña a la gente a desconfiar de ellos y a borrarlos. Prueba el comportamiento, y el test sigue siendo útil.

_Diagram: Un buen test mira lo que entra en getDiscount y lo que sale. Un mal test se mete en los ayudantes internos — y se rompe en cada refactor._

#### Test Behaviour, Not Implementation (black-box testing)

- **Problema:** Los tests se rompen en cada refactor, aunque el comportamiento no cambió.
- **Idea:** Comprueba solo el resultado observable, a través de la interfaz pública.
- **Ejemplo:** Comprueba que `getDiscount` devolvió 90 — no qué ayudantes llamó por dentro.
- **Coste:** A veces un caso límite es más difícil de alcanzar solo con entrada y salida.
- **Cuándo usarlo:** Siempre, por defecto.
- **Relaciones:** Hace posible el refactor en **TDD**; los **Mocks** de más suelen atar los tests a la implementación.

### Living Documentation — los tests como documentación

> Un conjunto de tests bien escrito se lee como una lista de promesas: «dado esto, hace aquello» — más claro que cualquier comentario sobre cómo debe comportarse el código.

Un conjunto de tests claro hace de **documentación viva**. Cada test enuncia una promesa — «dado un carrito vacío, el total es cero» — y como los tests se ejecutan, esa documentación nunca puede quedarse anticuada como los comentarios; si cambia el comportamiento, el test se rompe. Así que escribe los tests para que se lean: nombres claros, un comportamiento cada uno, un arrange-act-assert evidente. Un recién llegado debería poder aprender qué hace el código leyendo sus tests. Esa legibilidad vale tanto como la comprobación.

#### Living Documentation (especificación ejecutable)

- **Problema:** Los comentarios y las wikis mienten: el código cambió, el texto se quedó viejo.
- **Idea:** Los tests son promesas escritas como código. Rompes una promesa — el test se pone en rojo.
- **Ejemplo:** Un test llamado «carrito vacío — el total es cero». Se lee como una especificación.
- **Coste:** Tiempo para nombres claros y un comportamiento por test.
- **Cuándo usarlo:** Siempre — sobre todo en código que leen los recién llegados.
- **Relaciones:** Se apoya en **Arrange–Act–Assert** y **Test Behaviour**.

> **La pregunta del TDD:** «¿Describe este test el comportamiento que prometí — legible, centrado en el resultado — para que sobreviva a un refactor y enseñe a la siguiente persona qué hace el código?»

> Escribe primero el test (red-green-refactor), prueba el comportamiento y no la implementación, y hazlo legible — para que los tests sobrevivan a los refactors y sirvan de documentación que no se queda obsoleta.

## 6. Coverage y tests flaky

Un número y una molestia deciden si un conjunto de tests es de verdad fiable. Si lees mal el coverage o toleras la inestabilidad, un conjunto en verde deja de significar nada.

### Code Coverage — qué está probado, no lo bien que está

> Un mapa que muestra por qué calles pasó una patrulla — útil para ver los barrios nunca visitados, pero pasar delante de una casa no significa que comprobaras las cerraduras.

El **code coverage** es el porcentaje de tu código que se ejecuta durante los tests. Es una guía útil para encontrar lo que está _totalmente_ sin probar — las calles que nadie patrulló. Pero un coverage alto no significa buenos tests: un test puede ejecutar el código sin afirmar nada significativo, sacando un 100% mientras verifica poco. Usa el coverage para encontrar puntos ciegos, no como prueba de calidad. Que una línea se toque no es lo mismo que que una línea se compruebe.

_Diagram: 75% de coverage: 6 de 8 líneas se ejecutaron. Pero solo 3 las comprueba de verdad una assertion._

#### Code Coverage (cobertura de tests)

- **Qué es:** La parte de las líneas (o ramas) que se ejecutaron durante los tests.
- **Sirve para:** Mostrar puntos ciegos — código que ningún test ejecutó nunca.
- **Trampa:** No mide la calidad: una línea puede ejecutarse sin que ninguna assertion la compruebe.
- **Ejemplo:** 80% de coverage, pero toda la lógica de descuentos está en el 20% sin cubrir — ahí está el riesgo.
- **Cuándo usarlo:** Como linterna para los huecos, no como objetivo.
- **Relaciones:** Se vuelve una trampa por la **Goodhart's Law**; no sustituye al **Risk-based Testing**.

### Goodhart's Law — no dejes que el coverage sea el objetivo

> Pagar a los obreros por milla de carretera pintada, y verlos pintar líneas largas e inútiles en descampados para llegar al número — la métrica subió, las carreteras no fueron más seguras.

Cuando un número se convierte en objetivo, la gente optimiza el número en lugar de aquello que medía — esa es la **ley de Goodhart**. Exige «100% de coverage» y obtendrás tests escritos para tocar líneas, no para cazar bugs: tests huecos que no afirman nada, que juegan con la métrica. El coverage es una linterna para encontrar huecos, no un trofeo que maximizar. Persigue la confianza real — ¿los tests cazan de verdad las roturas? — y deja que el coverage sea una pista entre varias, nunca el objetivo en sí.

_Diagram: Una medida se convierte en objetivo — y nacen los tests huecos: el número sube, los bugs se quedan._

#### Goodhart's Law (medida vs objetivo)

- **Esencia:** Cuando una medida se convierte en objetivo, deja de ser una buena medida.
- **En testing:** Una regla «ningún merge sin 100% de coverage» cría tests sin assertions — solo por el número.
- **Ejemplo:** Un test llama a la función y no comprueba nada — el coverage sube, la protección es cero.
- **Remedio:** Pregunta «¿cazaría este test una rotura?» y trata el coverage como una pista.
- **Relaciones:** La principal trampa del **Code Coverage**.

### Flaky Test — peor que no tener test

> Una alarma de incendios que salta al azar sin motivo: en una semana todos la ignoran por completo, así que cuando hay un incendio de verdad, nadie se mueve.

Un **flaky test** es uno que pasa y falla al azar sin que el código cambie — normalmente por tiempos, orden o una dependencia oculta. Los flaky tests son veneno: cuando un resultado en rojo puede ser solo ruido, la gente deja de confiar en cualquier rojo, y un fallo real se despacha con un «seguro que es solo flaky». Un test debe ser **determinista** — mismo código, mismo resultado, siempre. Arregla los flaky tests o bórralos; un conjunto en el que no confías no protege nada.

_Diagram: El mismo código, seis ejecuciones — y dos fallos aleatorios. Después de eso, todo rojo se descarta como ruido._

#### Flaky Test (test inestable)

- **Qué es:** Un test que a veces pasa y a veces falla, sin cambios en el código.
- **Causas:** Tiempos (sleeps, carreras), orden de los tests, una dependencia compartida oculta.
- **Ejemplo:** Un test espera 100 ms a un servicio; en una máquina de CI ocupada a veces se rinde demasiado pronto.
- **Daño:** Enseña al equipo a ignorar el rojo — y un fallo real se cuela.
- **Remedio:** Haz el test **determinista** — o bórralo. Nunca convivas con un flaky test.
- **Relaciones:** Se siente más en casa en **E2E**; destroza la confianza en el **CI Gate**.

> **La pregunta de la confianza:** «¿Es mi conjunto de tests realmente fiable — con el coverage usado como pista y no como objetivo, y cero flaky tests — de modo que un verde signifique de verdad que es seguro publicar?»

> El coverage muestra qué está probado, no lo bien que está — no lo conviertas en el objetivo. Y un flaky test es peor que ninguno: cuando el rojo puede ser ruido, todo rojo se ignora.

## 7. Usarlo bien

Testear es criterio, no ritual. La habilidad está en probar lo que importa, en las proporciones justas, y en engancharlo a cómo publicas — para que el conjunto gane confianza en lugar de convertirse en trabajo de relleno.

### Risk-based Testing — prueba las partes de riesgo, no todo

> Compruebas tres veces el paracaídas y los frenos; no le haces una prueba de resistencia al portavasos. El esfuerzo va donde el fallo de verdad duele.

No todo el código merece el mismo testing. El **risk-based testing** significa concentrar el esfuerzo donde un bug sería costoso o probable: la lógica de negocio central, el dinero, la seguridad, los casos límite delicados, las partes que cambian a menudo. El código trivial y de bajo riesgo puede recibir menos. Perseguir tests para todo malgasta esfuerzo y crea un conjunto tan grande y lento que nadie lo ejecuta. Apunta a tests que cacen los fallos que de verdad lamentarías, no a un número — cubrir lo que importa vale más que cubrirlo todo.

_Diagram: Cuanto más costoso el fallo y más probable la rotura, más tests. El dinero, la seguridad y la lógica central están en la esquina superior derecha._

#### Risk-based Testing (testing por riesgo)

- **Problema:** El tiempo para testear es escaso, y intentar cubrirlo todo lo reparte demasiado fino.
- **Idea:** Más tests donde el fallo es costoso o probable; menos en lo trivial.
- **Ejemplo:** Las cuentas de pagos y los permisos de acceso — muy probados; los getters y el maquetado simple — lo mínimo.
- **Coste:** Tienes que juzgar el riesgo con honestidad, no probar lo que sea fácil.
- **Cuándo usarlo:** Cada vez que decides dónde gastar el tiempo de testing.
- **Relaciones:** Sustituye la persecución del **Code Coverage**; más denso como **Unit Tests** sobre la lógica.

### CI Gate — tests en cada cambio, automáticamente

> Un torniquete que no se abre si tu billete no es válido — la comprobación está integrada en la puerta, no depende de que alguien se acuerde de mirar.

Los tests solo te protegen si de verdad se ejecutan. Engánchalos a tu **pipeline** para que se ejecuten automáticamente en cada cambio y bloqueen el merge si fallan — el **CI gate** del curso de deploy. Esto es lo que convierte los tests de algo que quizá ejecutes en una garantía de que nada roto se fusiona. «Tests o no se publica» es el estándar que vale la pena mantener: un cambio sin probar yendo a producción es una apuesta que no tenías por qué hacer.

_Diagram: Todo cambio pasa por CI: todos los tests en verde — merge; aunque sea uno en rojo — el merge se bloquea._

#### CI Gate (comprobación de merge)

- **Problema:** Los tests existen, pero la gente se olvida de ejecutarlos — y el código roto aterriza en main.
- **Idea:** CI ejecuta todos los tests en cada cambio y rechaza el merge hasta que estén en verde.
- **Ejemplo:** Un pull request: CI ejecuta los tests, uno falla — el botón de merge queda desactivado.
- **Coste:** Esperar a CI; los flaky tests duelen más aquí.
- **Cuándo usarlo:** En cualquier proyecto con más de un cambio a la semana.
- **Relaciones:** Ejecuta toda la **Test Pyramid**; no puede convivir con los **Flaky Tests**; caza **Regressions** antes que los usuarios.

### Cómo encaja todo

> Una orquesta: cada instrumento importa, pero la música solo sucede juntos.

Los conceptos de este curso funcionan como un solo sistema. La **pirámide** fija la forma del conjunto de tests, el **TDD** lo escribe, los **test doubles** dan aislamiento a los unit tests, y el **CI gate** lo ejecuta todo en cada cambio — y eso entrega el premio principal: cambiar el código sin miedo. En contra trabajan los **flaky tests** y el **over-mocking**, que minan la confianza en el verde. Y el **coverage** solo insinúa dónde están los huecos.

_Diagram: La pirámide, el TDD y los test doubles construyen el conjunto; el CI gate lo ejecuta y te deja cambiar sin miedo. Los flaky tests y los mocks de más golpean la confianza; el coverage solo insinúa._

#### Antes de confiar en un conjunto de tests

- **¿Te deja cambiar con confianza** — cazaría una regression mañana?
- **¿Tiene forma de pirámide** — muchos unit, algunos integration, unos pocos e2e?
- **¿Los tests comprueban el comportamiento**, no la implementación interna que se rompe en un refactor?
- **¿Se usan mocks para dependencias externas reales**, no para falsear lo que se está probando?
- **¿Es rápido y determinista** — sin flaky tests que erosionen la confianza?
- **¿Se ejecuta en CI** en cada cambio, bloqueando los merges rotos?

#### Señales de que testeas bien

- **Cambias el código con confianza**, fiándote de que el conjunto cazará lo que rompiste.
- Tus tests forman una **pirámide** — rápidos en la base, unos pocos realistas arriba.
- Los tests comprueban el **comportamiento** y sobreviven a los refactors en lugar de romperse con cada limpieza.
- El conjunto es **rápido y determinista**, y arreglas o borras los tests **flaky**.
- Los tests **se ejecutan en CI** en cada cambio, y tratas el coverage como una pista, no como un objetivo.

## Chuleta: los 19 conceptos

### Por qué testeamos

- **Automated Test** (test): Código que comprueba código: entrada → llamada → assertion → pasa o falla.
- **Regression** (regression bug): Un cambio rompe lo que antes funcionaba, a menudo lejos de la edición.

### Unit tests

- **Unit Test** (test de módulo): Una función en aislamiento, en milisegundos. Tienes miles.
- **Arrange–Act–Assert** (AAA): Prepara, haz una cosa, comprueba el resultado.

### Integration, e2e y la pirámide

- **Integration Test** (comprobación de integración): Varias partes reales juntas — comprobando las costuras.
- **End-to-End Test** (E2E): Todo el sistema como lo ve un usuario. Realista, pero lento y frágil.
- **Test Pyramid** (pirámide de testing): Muchos unit, menos integration, unos pocos e2e. No el cono de helado.

### Falsos

- **Test Double** (falso): Un sustituto de una dependencia lenta, cara o aleatoria.
- **Stub** (respuesta enlatada): Devuelve una respuesta preparada — alimenta a tu código con datos.
- **Mock** (registrador de llamadas): Registra llamadas — verifica que ocurrió un efecto secundario.
- **Over-mocking** (demasiados mocks): Todo con mock — el test está en verde mientras el sistema real falla.

### TDD y buenos tests

- **TDD** (red-green-refactor): Primero un test que falla, luego el mínimo código, luego una limpieza.
- **Test Behaviour, Not Implementation** (black-box testing): Comprueba el resultado por la interfaz pública — el test sobrevive al refactor.
- **Living Documentation** (especificación ejecutable): Los tests son promesas en código que nunca se quedan obsoletas.

### Confiar en el conjunto

- **Code Coverage** (cobertura de tests): Cuánto código se ejecutó en los tests. Una linterna para los huecos, no una medida de calidad.
- **Goodhart's Law** (medida vs objetivo): Una medida que se vuelve objetivo deja de medir. 100% de coverage → tests huecos.
- **Flaky Test** (test inestable): Falla al azar. El remedio: hacerlo determinista, o borrarlo.

### Usarlo bien

- **Risk-based Testing** (testing por riesgo): Más tests donde el fallo es costoso o probable.
- **CI Gate** (comprobación de merge): Tests en cada cambio; rojo — el merge se bloquea.

> Testear bien es criterio: prueba las partes de riesgo, mantén la pirámide, comprueba el comportamiento y no la implementación, y ejecútalo todo automáticamente — para que un conjunto en verde signifique de verdad que es seguro cambiar.

Fin del curso exprés · 7 capítulos · 19 conceptos

Lo que sigue es práctica: toma una función importante y escribe unos cuantos unit tests para ella — arrange, act, assert — luego rompe la función a propósito y mira cómo un test se pone en rojo y te dice exactamente qué. Después añade un test para el siguiente bug que encuentres, para que el conjunto crezca a lo largo de los fallos que de verdad ocurren. Pero quédate con una idea por encima de las demás: los tests no sirven para probar que el código de hoy funciona. Son el arnés de seguridad que te deja cambiar mañana sin miedo — y esa libertad de cambiar con confianza es lo que permite que el buen software siga creciendo en lugar de anquilosarse poco a poco.
