567 lines
21 KiB
Markdown
567 lines
21 KiB
Markdown
|
|
# Desarrollo guiado por pruebas (Test-Driven Development)
|
||
|
|
|
||
|
|
El desarrollo guiado por pruebas (TDD) es una metodología que utiliza pruebas automatizadas para orientar la implementación y evolución del software.
|
||
|
|
|
||
|
|
El objetivo no es simplemente aumentar el número de pruebas, sino diseñar código verificable, expresar claramente los comportamientos esperados y reducir el riesgo de introducir regresiones.
|
||
|
|
|
||
|
|
Este documento desarrolla los principios definidos en el `SKILL.md` principal.
|
||
|
|
|
||
|
|
**TDD es la metodología preferente cuando resulta apropiada, pero no debe imponerse de forma mecánica si existe una alternativa de verificación más adecuada.**
|
||
|
|
|
||
|
|
## 1. El ciclo fundamental de TDD
|
||
|
|
|
||
|
|
```text
|
||
|
|
ROJO → VERDE → REFACTORIZAR → ROJO → ...
|
||
|
|
```
|
||
|
|
|
||
|
|
Cada iteración debe ser pequeña y centrarse en un comportamiento concreto.
|
||
|
|
|
||
|
|
### 1.1. Fase ROJO (RED)
|
||
|
|
|
||
|
|
Escribe una prueba que falle y describa el comportamiento que deseas implementar.
|
||
|
|
|
||
|
|
La prueba debe:
|
||
|
|
|
||
|
|
- Utilizar lenguaje del dominio, evitando detalles técnicos innecesarios.
|
||
|
|
- Describir **QUÉ** debe ocurrir, no **CÓMO** debe implementarse.
|
||
|
|
- Representar un ejemplo concreto y verificable.
|
||
|
|
- Fallar por la ausencia del comportamiento esperado, no por errores accidentales de configuración.
|
||
|
|
- Expresar claramente el resultado esperado.
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
// MAL: Descripción demasiado abstracta.
|
||
|
|
it('can add numbers', () => {
|
||
|
|
expect(add(2, 3)).toBe(5);
|
||
|
|
});
|
||
|
|
|
||
|
|
// BIEN: Ejemplo concreto.
|
||
|
|
it('returns 5 when adding 2 and 3', () => {
|
||
|
|
expect(add(2, 3)).toBe(5);
|
||
|
|
});
|
||
|
|
```
|
||
|
|
|
||
|
|
Aunque las descripciones de pruebas son cadenas de texto, sigue las convenciones del proyecto. Si se consideran documentación del comportamiento, pueden redactarse en castellano.
|
||
|
|
|
||
|
|
**Comprueba que la prueba falla por el motivo esperado antes de continuar.**
|
||
|
|
|
||
|
|
Una prueba que falla por un error de importación, una dependencia ausente o una configuración incorrecta no demuestra necesariamente que el comportamiento esté pendiente de implementar.
|
||
|
|
|
||
|
|
### 1.2. Fase VERDE (GREEN)
|
||
|
|
|
||
|
|
Escribe la implementación más sencilla que permita superar la prueba.
|
||
|
|
|
||
|
|
Evita introducir funcionalidades, configuraciones o abstracciones que todavía no sean necesarias.
|
||
|
|
|
||
|
|
Existen dos estrategias habituales.
|
||
|
|
|
||
|
|
**Estrategia 1: Fake It (simular inicialmente el resultado)**
|
||
|
|
|
||
|
|
Consiste en utilizar una implementación provisional mínima para superar un ejemplo concreto.
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
function add(a: number, b: number): number {
|
||
|
|
return 5;
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
Esta implementación únicamente satisface un ejemplo específico.
|
||
|
|
|
||
|
|
Puede utilizarse como paso temporal durante el aprendizaje de TDD o cuando se busca descubrir una generalización mediante pruebas adicionales.
|
||
|
|
|
||
|
|
**No debe conservarse como implementación final si el comportamiento requerido exige una solución general.**
|
||
|
|
|
||
|
|
**Estrategia 2: Obvious Implementation (implementación evidente)**
|
||
|
|
|
||
|
|
Cuando la solución es sencilla y conocida, implementa directamente el comportamiento.
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
function add(a: number, b: number): number {
|
||
|
|
return a + b;
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
No es necesario utilizar *Fake It* si la implementación correcta resulta evidente.
|
||
|
|
|
||
|
|
### Criterios para elegir la estrategia
|
||
|
|
|
||
|
|
- Utiliza *Fake It* cuando necesites avanzar incrementalmente hacia una generalización.
|
||
|
|
- Utiliza *Obvious Implementation* cuando el comportamiento sea sencillo y conocido.
|
||
|
|
- Evita construir una arquitectura completa a partir de un único ejemplo.
|
||
|
|
- No introduzcas casos hipotéticos sin una necesidad identificada.
|
||
|
|
- Asegúrate de que el conjunto de pruebas comprueba el comportamiento realmente requerido.
|
||
|
|
|
||
|
|
**La simplicidad debe evaluarse respecto al comportamiento esperado, no únicamente respecto a la cantidad de código.**
|
||
|
|
|
||
|
|
### 1.3. Fase REFACTORIZAR (REFACTOR)
|
||
|
|
|
||
|
|
Una vez que las pruebas pasan, mejora el diseño sin modificar el comportamiento observable.
|
||
|
|
|
||
|
|
Revisa:
|
||
|
|
|
||
|
|
- Duplicación de conocimiento o reglas de negocio.
|
||
|
|
- Métodos o clases con responsabilidades mezcladas.
|
||
|
|
- Nombres poco expresivos.
|
||
|
|
- Condiciones difíciles de comprender.
|
||
|
|
- Dependencias innecesarias.
|
||
|
|
- Abstracciones prematuras.
|
||
|
|
- Estructuras que dificulten el mantenimiento.
|
||
|
|
|
||
|
|
La refactorización debe realizarse en incrementos pequeños, verificando que las pruebas continúan pasando.
|
||
|
|
|
||
|
|
**No refactorices únicamente para satisfacer métricas arbitrarias de líneas, atributos o número de métodos.**
|
||
|
|
|
||
|
|
El diseño puede evolucionar durante la refactorización, pero también requiere decisiones previas cuando sea necesario definir contratos, límites entre módulos o responsabilidades arquitectónicas.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 2. Las tres leyes del TDD
|
||
|
|
|
||
|
|
Durante la aplicación estricta del ciclo TDD:
|
||
|
|
|
||
|
|
1. **No escribas código de producción** salvo para conseguir que una prueba que falla pase correctamente.
|
||
|
|
2. **No escribas más código de prueba** del necesario para demostrar el fallo del comportamiento esperado.
|
||
|
|
3. **No escribas más código de producción** del necesario para superar la prueba que falla.
|
||
|
|
|
||
|
|
Estas leyes favorecen los incrementos pequeños y evitan implementar funcionalidades no solicitadas.
|
||
|
|
|
||
|
|
No deben interpretarse como una prohibición universal de escribir código cuando TDD no sea la metodología apropiada.
|
||
|
|
|
||
|
|
### Excepciones justificadas
|
||
|
|
|
||
|
|
Puede utilizarse otro procedimiento cuando:
|
||
|
|
|
||
|
|
- Se modifique configuración, documentación o infraestructura sin comportamiento directamente comprobable mediante pruebas unitarias.
|
||
|
|
- Se trabaje con código legado sin infraestructura de pruebas adecuada.
|
||
|
|
- Se realicen prototipos exploratorios.
|
||
|
|
- Sea necesario corregir urgentemente un problema y no resulte viable escribir primero una prueba.
|
||
|
|
- Se efectúen modificaciones estructurales cubiertas por pruebas existentes.
|
||
|
|
|
||
|
|
En estos casos:
|
||
|
|
|
||
|
|
1. Identifica el comportamiento que debe mantenerse o modificarse.
|
||
|
|
2. Selecciona el mecanismo de verificación más adecuado.
|
||
|
|
3. Realiza cambios pequeños y controlados.
|
||
|
|
4. Ejecuta las comprobaciones disponibles.
|
||
|
|
5. Añade pruebas de regresión cuando resulte técnicamente viable.
|
||
|
|
|
||
|
|
**No utilizar TDD no significa trabajar sin verificación.**
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 3. La regla de tres
|
||
|
|
|
||
|
|
La regla de tres es una heurística para identificar oportunidades de abstracción.
|
||
|
|
|
||
|
|
Tradicionalmente propone esperar hasta observar varias repeticiones antes de extraer código común.
|
||
|
|
|
||
|
|
Su propósito es evitar crear abstracciones incorrectas a partir de similitudes superficiales.
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
// Primera aparición: implementar directamente.
|
||
|
|
|
||
|
|
// Segunda aparición: comprobar si existe conocimiento compartido.
|
||
|
|
|
||
|
|
// Tercera aparición: reconsiderar si conviene extraer una abstracción.
|
||
|
|
```
|
||
|
|
|
||
|
|
### Aplicación recomendada
|
||
|
|
|
||
|
|
- No extraigas automáticamente código después de la tercera repetición.
|
||
|
|
- No esperes obligatoriamente a tres repeticiones cuando ya exista una regla de negocio compartida.
|
||
|
|
- Comprueba que las implementaciones representen el mismo concepto.
|
||
|
|
- Evita abstraer código que únicamente se parece sintácticamente.
|
||
|
|
- Valora el coste de mantenimiento de la duplicación frente al coste de la abstracción.
|
||
|
|
|
||
|
|
**La duplicación de conocimiento puede ser más peligrosa que la duplicación de instrucciones.**
|
||
|
|
|
||
|
|
Por ejemplo, dos implementaciones independientes de una misma regla fiscal pueden justificar una extracción inmediata.
|
||
|
|
|
||
|
|
En cambio, tres operaciones similares pertenecientes a dominios diferentes pueden mantenerse separadas.
|
||
|
|
|
||
|
|
> "Un poco de duplicación es mejor que una abstracción equivocada."
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 4. Triangulación
|
||
|
|
|
||
|
|
La triangulación consiste en utilizar varios ejemplos concretos para orientar la implementación hacia una solución general.
|
||
|
|
|
||
|
|
Cada prueba adicional restringe las implementaciones posibles y ayuda a descubrir reglas que satisfacen todos los casos.
|
||
|
|
|
||
|
|
### Ejemplo
|
||
|
|
|
||
|
|
Primera prueba:
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
it('returns 5 when adding 2 and 3', () => {
|
||
|
|
expect(add(2, 3)).toBe(5);
|
||
|
|
});
|
||
|
|
```
|
||
|
|
|
||
|
|
Una implementación provisional podría devolver siempre `5`.
|
||
|
|
|
||
|
|
Segunda prueba:
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
it('returns 9 when adding 4 and 5', () => {
|
||
|
|
expect(add(4, 5)).toBe(9);
|
||
|
|
});
|
||
|
|
```
|
||
|
|
|
||
|
|
Esta prueba demuestra que devolver una constante no satisface el comportamiento general.
|
||
|
|
|
||
|
|
La implementación debe evolucionar:
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
function add(a: number, b: number): number {
|
||
|
|
return a + b;
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### Criterios de triangulación
|
||
|
|
|
||
|
|
- Añade ejemplos que aporten información nueva.
|
||
|
|
- Incluye casos límite cuando sean relevantes.
|
||
|
|
- Evita pruebas redundantes que no aumenten la confianza.
|
||
|
|
- No generes combinaciones arbitrarias sin relación con los requisitos.
|
||
|
|
- Utiliza la triangulación para descubrir comportamientos, no para retrasar artificialmente una solución evidente.
|
||
|
|
|
||
|
|
La triangulación es especialmente útil cuando las reglas del dominio son complejas o todavía no están completamente definidas.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 5. Prioridad de las transformaciones (Transformation Priority Premise)
|
||
|
|
|
||
|
|
La *Transformation Priority Premise* propone avanzar desde implementaciones simples hacia otras más complejas mediante transformaciones pequeñas.
|
||
|
|
|
||
|
|
Es una herramienta de aprendizaje y razonamiento, no un algoritmo obligatorio de implementación.
|
||
|
|
|
||
|
|
| Prioridad orientativa | Transformación |
|
||
|
|
|---|---|
|
||
|
|
| 1 | Estructura vacía → ausencia de valor (`nil`/`null`) |
|
||
|
|
| 2 | Ausencia de valor → constante |
|
||
|
|
| 3 | Constante → variable |
|
||
|
|
| 4 | Comportamiento incondicional → condicional |
|
||
|
|
| 5 | Valor escalar → colección |
|
||
|
|
| 6 | Instrucción → recursión |
|
||
|
|
| 7 | Valor → valor modificado |
|
||
|
|
|
||
|
|
### Criterios de aplicación
|
||
|
|
|
||
|
|
- Prefiere transformaciones pequeñas cuando reduzcan el riesgo.
|
||
|
|
- No introduzcas complejidad antes de que resulte necesaria.
|
||
|
|
- No fuerces una secuencia concreta de transformaciones.
|
||
|
|
- Utiliza directamente una implementación evidente cuando sea más clara.
|
||
|
|
- Evalúa la transformación según las características del lenguaje y del dominio.
|
||
|
|
|
||
|
|
**La prioridad no debe interpretarse como una clasificación universal de complejidad o calidad.**
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 6. Patrón Preparar-Actuar-Verificar (Arrange-Act-Assert)
|
||
|
|
|
||
|
|
La referencia canónica sobre AAA y nomenclatura es [testing.md](testing.md), secciones 3 y 4. Aquí se conserva su aplicación al ciclo TDD y al diseño de la prueba en orden inverso.
|
||
|
|
|
||
|
|
El patrón AAA estructura las pruebas en tres fases:
|
||
|
|
|
||
|
|
1. **Preparar (Arrange):** establecer el estado y los datos necesarios.
|
||
|
|
2. **Actuar (Act):** ejecutar el comportamiento que se desea comprobar.
|
||
|
|
3. **Verificar (Assert):** comprobar el resultado esperado.
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
it('applies a 10 percent discount to the order total', () => {
|
||
|
|
// Preparar: Crear el pedido y el descuento.
|
||
|
|
const order = new Order();
|
||
|
|
order.addItem({ price: 100 });
|
||
|
|
|
||
|
|
const discount = new PercentDiscount(10);
|
||
|
|
|
||
|
|
// Actuar: Calcular el importe final.
|
||
|
|
const total = order.calculateTotal(discount);
|
||
|
|
|
||
|
|
// Verificar: Comprobar el resultado.
|
||
|
|
expect(total).toBe(90);
|
||
|
|
});
|
||
|
|
```
|
||
|
|
|
||
|
|
El ejemplo presupone que el dominio utiliza importes numéricos. En un sistema con reglas monetarias específicas puede ser preferible utilizar objetos de valor como `Money`.
|
||
|
|
|
||
|
|
### Buenas prácticas
|
||
|
|
|
||
|
|
- Mantén claramente identificable el comportamiento probado.
|
||
|
|
- Evita configuraciones extensas que oculten la intención.
|
||
|
|
- Utiliza funciones auxiliares o factorías de pruebas cuando simplifiquen la preparación.
|
||
|
|
- No introduzcas abstracciones de pruebas innecesarias.
|
||
|
|
- Mantén las verificaciones relacionadas con el comportamiento esperado.
|
||
|
|
|
||
|
|
No es obligatorio escribir comentarios para separar las tres fases cuando la estructura resulte evidente.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 7. Escribir pruebas en orden inverso
|
||
|
|
|
||
|
|
En ocasiones resulta útil diseñar una prueba comenzando por la verificación.
|
||
|
|
|
||
|
|
El procedimiento consiste en:
|
||
|
|
|
||
|
|
1. **ASSERT:** definir qué resultado debe comprobarse.
|
||
|
|
2. **ACT:** identificar qué operación produce ese resultado.
|
||
|
|
3. **ARRANGE:** preparar únicamente los datos y dependencias necesarios.
|
||
|
|
|
||
|
|
### Ejemplo
|
||
|
|
|
||
|
|
Primero, define la expectativa:
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
expect(total).toBe(90);
|
||
|
|
```
|
||
|
|
|
||
|
|
Después, identifica la operación:
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
const total = order.calculateTotal(discount);
|
||
|
|
```
|
||
|
|
|
||
|
|
Por último, prepara el escenario:
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
const order = new Order();
|
||
|
|
order.addItem({ price: 100 });
|
||
|
|
|
||
|
|
const discount = new PercentDiscount(10);
|
||
|
|
```
|
||
|
|
|
||
|
|
Esta técnica ayuda a evitar configuraciones innecesarias y favorece pruebas centradas en resultados observables.
|
||
|
|
|
||
|
|
No es obligatorio utilizarla cuando el comportamiento ya esté suficientemente claro.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 8. Nomenclatura de las pruebas
|
||
|
|
|
||
|
|
Los nombres deben describir comportamientos concretos utilizando el lenguaje del dominio.
|
||
|
|
|
||
|
|
### Principios
|
||
|
|
|
||
|
|
- Describe el comportamiento esperado.
|
||
|
|
- Utiliza ejemplos concretos cuando aporten claridad.
|
||
|
|
- Evita referencias innecesarias a detalles internos.
|
||
|
|
- Mantén una relación clara entre el nombre y la verificación realizada.
|
||
|
|
- Utiliza nombres coherentes con las convenciones del proyecto.
|
||
|
|
- Facilita identificar el fallo sin necesidad de inspeccionar toda la prueba.
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
// MAL: Se centra en un detalle interno.
|
||
|
|
it('sets the data property to 1', () => {
|
||
|
|
// ...
|
||
|
|
});
|
||
|
|
```
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
// BIEN: Describe un comportamiento observable.
|
||
|
|
it('recognizes "mom" as a palindrome', () => {
|
||
|
|
expect(isPalindrome('mom')).toBe(true);
|
||
|
|
});
|
||
|
|
```
|
||
|
|
|
||
|
|
### Una responsabilidad de prueba por caso
|
||
|
|
|
||
|
|
Como orientación, cada prueba debe verificar un comportamiento concreto.
|
||
|
|
|
||
|
|
Esto no significa limitarla a una única instrucción `expect`.
|
||
|
|
|
||
|
|
Una misma operación puede requerir comprobar varios efectos relacionados.
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
it('creates an order with the expected initial state', () => {
|
||
|
|
const order = createOrder();
|
||
|
|
|
||
|
|
expect(order.status).toBe('pending');
|
||
|
|
expect(order.items).toHaveLength(0);
|
||
|
|
expect(order.total).toBe(0);
|
||
|
|
});
|
||
|
|
```
|
||
|
|
|
||
|
|
Estas verificaciones pertenecen al mismo comportamiento inicial.
|
||
|
|
|
||
|
|
Evita agrupar escenarios independientes dentro de una única prueba extensa.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 9. TDD clásico y Mockist
|
||
|
|
|
||
|
|
Existen diferentes estilos de TDD. Ninguno debe imponerse universalmente.
|
||
|
|
|
||
|
|
### 9.1. TDD clásico (Detroit/Chicago)
|
||
|
|
|
||
|
|
Se centra principalmente en los estados y resultados observables.
|
||
|
|
|
||
|
|
Características:
|
||
|
|
|
||
|
|
- Prefiere utilizar objetos reales cuando resulta práctico.
|
||
|
|
- Evita simulaciones innecesarias.
|
||
|
|
- Comprueba resultados y cambios de estado.
|
||
|
|
- Permite que el diseño evolucione mediante el comportamiento.
|
||
|
|
|
||
|
|
Resulta especialmente apropiado para:
|
||
|
|
|
||
|
|
- Funciones puras.
|
||
|
|
- Objetos de dominio.
|
||
|
|
- Algoritmos.
|
||
|
|
- Reglas de negocio.
|
||
|
|
- Componentes que pueden probarse sin infraestructura externa.
|
||
|
|
|
||
|
|
El uso de objetos reales no convierte automáticamente una prueba en una prueba de integración.
|
||
|
|
|
||
|
|
### 9.2. TDD Mockist (London)
|
||
|
|
|
||
|
|
Se centra especialmente en las interacciones y colaboración entre componentes.
|
||
|
|
|
||
|
|
Características:
|
||
|
|
|
||
|
|
- Utiliza dobles de prueba para sustituir determinadas dependencias.
|
||
|
|
- Permite verificar colaboraciones relevantes.
|
||
|
|
- Facilita aislar componentes.
|
||
|
|
- Puede favorecer diseños basados en interfaces y responsabilidades explícitas.
|
||
|
|
|
||
|
|
Resulta útil cuando:
|
||
|
|
|
||
|
|
- Existen dependencias externas.
|
||
|
|
- Deben verificarse interacciones relevantes.
|
||
|
|
- Es necesario controlar respuestas o errores de colaboradores.
|
||
|
|
- La infraestructura dificulta ejecutar pruebas rápidas y deterministas.
|
||
|
|
|
||
|
|
### Criterios para utilizar mocks
|
||
|
|
|
||
|
|
- No simules dependencias únicamente por costumbre.
|
||
|
|
- Evita simular objetos de dominio sencillos que puedan utilizarse directamente.
|
||
|
|
- Utiliza dobles de prueba en los límites con bases de datos, servicios externos, sistemas de archivos u otras dependencias cuando resulte apropiado.
|
||
|
|
- No hagas que las pruebas dependan excesivamente de llamadas internas irrelevantes.
|
||
|
|
- Comprueba que las simulaciones respetan los contratos reales de sus dependencias.
|
||
|
|
- Complementa las pruebas aisladas con pruebas de integración cuando sea necesario.
|
||
|
|
|
||
|
|
**Una prueba que verifica demasiados detalles de implementación puede fallar ante una refactorización correcta, aunque el comportamiento observable no haya cambiado.**
|
||
|
|
|
||
|
|
Selecciona el enfoque que proporcione mayor confianza con un coste razonable de mantenimiento.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 10. TDD en código legado
|
||
|
|
|
||
|
|
El código legado puede carecer de pruebas, presentar dependencias difíciles de aislar o contener comportamientos implícitos.
|
||
|
|
|
||
|
|
En estos escenarios, no siempre resulta apropiado comenzar con una prueba unitaria convencional.
|
||
|
|
|
||
|
|
### Procedimiento recomendado
|
||
|
|
|
||
|
|
1. Identifica el comportamiento que debe modificarse.
|
||
|
|
2. Analiza sus consumidores, contratos y dependencias.
|
||
|
|
3. Comprueba qué pruebas y herramientas de verificación existen.
|
||
|
|
4. Cuando sea viable, escribe pruebas de caracterización para documentar el comportamiento actual.
|
||
|
|
5. Introduce los cambios de forma incremental.
|
||
|
|
6. Añade pruebas de regresión para el comportamiento corregido o incorporado.
|
||
|
|
7. Ejecuta las verificaciones disponibles.
|
||
|
|
|
||
|
|
### Pruebas de caracterización
|
||
|
|
|
||
|
|
Una prueba de caracterización describe el comportamiento actual de un componente, aunque este no sea necesariamente el comportamiento ideal.
|
||
|
|
|
||
|
|
Su objetivo es detectar cambios accidentales durante una refactorización.
|
||
|
|
|
||
|
|
No debe utilizarse para convertir automáticamente un error conocido en un requisito permanente.
|
||
|
|
|
||
|
|
Cuando el objetivo sea corregir un error, distingue entre:
|
||
|
|
|
||
|
|
- Comportamiento existente que debe preservarse.
|
||
|
|
- Comportamiento defectuoso que debe corregirse.
|
||
|
|
- Comportamiento nuevo que debe incorporarse.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 11. Pruebas de regresión
|
||
|
|
|
||
|
|
Al corregir un defecto reproducible, es recomendable incorporar una prueba que detecte su reaparición.
|
||
|
|
|
||
|
|
### Procedimiento
|
||
|
|
|
||
|
|
1. Reproduce el defecto.
|
||
|
|
2. Identifica el comportamiento esperado.
|
||
|
|
3. Escribe una prueba que falle por el defecto, cuando sea viable.
|
||
|
|
4. Corrige la implementación.
|
||
|
|
5. Comprueba que la prueba pasa.
|
||
|
|
6. Ejecuta las pruebas relacionadas para detectar regresiones.
|
||
|
|
|
||
|
|
Evita modificar o eliminar pruebas existentes únicamente para conseguir que la nueva implementación pase.
|
||
|
|
|
||
|
|
Si el comportamiento anterior era incorrecto y las pruebas lo reflejaban, actualízalas justificando el cambio de requisito o la corrección del defecto.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 12. Errores habituales al aplicar TDD
|
||
|
|
|
||
|
|
| Error | Criterio de corrección |
|
||
|
|
|---|---|
|
||
|
|
| Escribir código antes de una prueba durante un ciclo TDD | Recuperar el ciclo Rojo-Verde-Refactorizar. |
|
||
|
|
| Escribir demasiadas pruebas antes de implementar | Trabajar en incrementos de comportamiento pequeños. |
|
||
|
|
| Implementar más funcionalidad de la necesaria | Limitarse a los requisitos y ejemplos relevantes. |
|
||
|
|
| Omitir sistemáticamente la refactorización | Revisar el diseño una vez superadas las pruebas. |
|
||
|
|
| Probar detalles internos | Priorizar comportamientos y contratos observables. |
|
||
|
|
| Utilizar nombres abstractos | Describir escenarios y resultados concretos. |
|
||
|
|
| Extraer abstracciones demasiado pronto | Esperar a comprender la responsabilidad compartida. |
|
||
|
|
| Aplicar literalmente la regla de tres | Evaluar duplicación de conocimiento y coste de mantenimiento. |
|
||
|
|
| Abusar de mocks | Utilizar objetos reales cuando resulten más sencillos y fiables. |
|
||
|
|
| Mantener implementaciones artificiales de *Fake It* | Generalizar cuando las pruebas y requisitos lo exijan. |
|
||
|
|
| Confiar únicamente en pruebas unitarias | Incorporar pruebas de integración o E2E cuando corresponda. |
|
||
|
|
| Modificar pruebas para ocultar defectos | Investigar el fallo y preservar los requisitos correctos. |
|
||
|
|
| Exigir TDD en cualquier circunstancia | Seleccionar la estrategia de verificación adecuada al contexto. |
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 13. Lista de comprobación de TDD
|
||
|
|
|
||
|
|
### Antes de implementar
|
||
|
|
|
||
|
|
- [ ] ¿Comprendo el comportamiento esperado?
|
||
|
|
- [ ] ¿Existen criterios de aceptación claros?
|
||
|
|
- [ ] ¿Qué nivel de prueba resulta más apropiado?
|
||
|
|
- [ ] ¿Puedo escribir primero una prueba que reproduzca el comportamiento pendiente?
|
||
|
|
- [ ] ¿Conozco el comportamiento existente que debo preservar?
|
||
|
|
- [ ] ¿Puedo utilizar la infraestructura de pruebas del proyecto?
|
||
|
|
|
||
|
|
### Durante la implementación
|
||
|
|
|
||
|
|
- [ ] ¿La prueba falla inicialmente por el motivo esperado?
|
||
|
|
- [ ] ¿La implementación satisface el comportamiento requerido?
|
||
|
|
- [ ] ¿Estoy evitando funcionalidades no solicitadas?
|
||
|
|
- [ ] ¿Las pruebas siguen siendo deterministas?
|
||
|
|
- [ ] ¿Estoy probando comportamientos en lugar de detalles internos?
|
||
|
|
- [ ] ¿He evitado mocks y abstracciones innecesarias?
|
||
|
|
|
||
|
|
### Durante la refactorización
|
||
|
|
|
||
|
|
- [ ] ¿Las pruebas continúan pasando?
|
||
|
|
- [ ] ¿Se conserva el comportamiento observable?
|
||
|
|
- [ ] ¿Las abstracciones introducidas representan conceptos reales?
|
||
|
|
- [ ] ¿He mejorado la comprensión sin fragmentar artificialmente el código?
|
||
|
|
- [ ] ¿He evitado cambios ajenos al alcance de la tarea?
|
||
|
|
|
||
|
|
### Al finalizar
|
||
|
|
|
||
|
|
- [ ] ¿Se satisfacen los criterios de aceptación?
|
||
|
|
- [ ] ¿Se han ejecutado las pruebas relevantes?
|
||
|
|
- [ ] ¿Se han incorporado pruebas de regresión cuando corresponde?
|
||
|
|
- [ ] ¿Se han verificado los contratos y comportamientos afectados?
|
||
|
|
- [ ] ¿He identificado las pruebas que no pudieron ejecutarse?
|
||
|
|
- [ ] ¿Puedo comunicar con precisión qué se ha verificado y qué permanece pendiente?
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Principio final
|
||
|
|
|
||
|
|
> "TDD es una herramienta para descubrir y verificar comportamientos, no un objetivo independiente de la calidad del software."
|
||
|
|
|
||
|
|
Utiliza el ciclo Rojo-Verde-Refactorizar cuando permita desarrollar de forma segura, incremental y comprensible.
|
||
|
|
|
||
|
|
No conviertas las reglas metodológicas en restricciones que impidan adoptar una solución técnicamente más apropiada.
|
||
|
|
|
||
|
|
**El objetivo es producir software correcto, verificable, sencillo y mantenible, utilizando las pruebas como una herramienta para mejorar tanto el diseño como la confianza en el comportamiento del sistema.**
|