5. Código mínimo para pasar la prueba sin adelantar diseño innecesario

5.1 Objetivo del tema

En el tema anterior escribimos pruebas rojas para un validador de contraseñas. En este tema daremos el siguiente paso: escribir el código mínimo necesario para que esas pruebas pasen.

La intención no es diseñar un validador completo de seguridad, sino practicar la etapa verde de TDD. En esta etapa buscamos que la prueba pase con la solución más simple posible, sin agregar reglas que todavía no fueron pedidas por una prueba.

Objetivo práctico: pasar de una prueba roja a una prueba verde escribiendo únicamente el código que la prueba actual justifica.

5.2 Punto de partida

Partimos de las pruebas escritas en el tema anterior. El archivo de pruebas expresa dos comportamientos:

  • Una contraseña con al menos 8 caracteres debe ser válida.
  • Una contraseña con menos de 8 caracteres debe ser inválida.

Archivo existente: tests/test_password.py

from seguridad.password import es_password_valido


def test_password_es_valido_si_tiene_al_menos_ocho_caracteres():
    password = "abcdefgh"

    resultado = es_password_valido(password)

    assert resultado is True


def test_password_es_invalido_si_tiene_menos_de_ocho_caracteres():
    password = "abc"

    resultado = es_password_valido(password)

    assert resultado is False

5.3 Ejecutar antes de tocar el código

Antes de implementar, ejecutamos las pruebas para confirmar el estado actual:

python -m pytest

Deberíamos ver un fallo de importación porque todavía no existe el módulo seguridad.password. Ese fallo nos indica el primer paso mínimo.

5.4 Primer paso mínimo: crear el módulo

No necesitamos implementar toda la lógica de validación todavía. El primer problema es que falta el módulo.

Archivo a crear: src/seguridad/password.py

Crea el archivo vacío. Por ahora no escribas ninguna función dentro.

Después de crear el archivo vacío, ejecutamos nuevamente:

python -m pytest

El fallo debería cambiar. Eso es progreso: dejamos de tener un problema de módulo inexistente y pasamos al siguiente problema concreto.

5.5 Segundo fallo esperado

Ahora es probable ver un error parecido a este:

ImportError: cannot import name 'es_password_valido'

El módulo ya existe, pero todavía no contiene la función que la prueba quiere usar.

5.6 Segundo paso mínimo: crear la función

Agregamos la función con la menor implementación posible.

Archivo a modificar: src/seguridad/password.py

def es_password_valido(password):
    return True

Ejecutamos:

python -m pytest

Con esta implementación, la prueba de contraseña válida debería pasar, pero la prueba de contraseña corta debería fallar.

5.7 Por qué devolver True puede ser correcto por un momento

Devolver siempre True parece una solución incompleta, y lo es. Pero nos permite pasar una parte del camino con un cambio mínimo.

En TDD no buscamos impresionar con una implementación sofisticada desde el primer intento. Buscamos avanzar con pasos pequeños y dejar que las pruebas nos indiquen cuándo hace falta generalizar.

5.8 La prueba que obliga a generalizar

La segunda prueba exige que una contraseña corta devuelva False. Ahora sí tenemos una razón concreta para cambiar la implementación.

Archivo a modificar: src/seguridad/password.py

def es_password_valido(password):
    return len(password) >= 8

Ejecutamos nuevamente:

python -m pytest

Ahora las dos pruebas deberían pasar.

5.9 Qué significa estar en verde

Estar en verde significa que todas las pruebas automatizadas pasan. En este punto, el comportamiento especificado por las pruebas está cubierto.

Pero verde no significa que el producto esté completo. Solo significa que el código cumple los ejemplos que escribimos hasta ahora.

5.10 No adelantar diseño

Podríamos agregar reglas como exigir números, mayúsculas, símbolos o prohibir espacios. Pero ninguna prueba pidió todavía esos comportamientos.

Si agregamos esas reglas ahora, podríamos romper casos que todavía no fueron definidos y complicar el diseño antes de tener necesidad real.

Regla práctica: si una nueva condición de negocio no está respaldada por una prueba, no la agregues durante la etapa verde.

5.11 Mejorar la prueba del caso límite

La contraseña "abcdefgh" tiene exactamente 8 caracteres. Eso es bueno porque prueba el límite mínimo. Podemos hacer más explícito ese dato en el nombre de la prueba.

Archivo a modificar: tests/test_password.py

from seguridad.password import es_password_valido


def test_password_es_valido_si_tiene_ocho_caracteres():
    password = "abcdefgh"

    resultado = es_password_valido(password)

    assert resultado is True


def test_password_es_invalido_si_tiene_menos_de_ocho_caracteres():
    password = "abc"

    resultado = es_password_valido(password)

    assert resultado is False

Después de renombrar la prueba, ejecutamos python -m pytest. Cambiamos claridad, no comportamiento.

5.12 Agregar el caso de más de ocho caracteres

El requisito dice “al menos 8 caracteres”. Ya probamos 8 y menos de 8. Podemos agregar un ejemplo con más de 8 caracteres para completar la intención.

Archivo a modificar: tests/test_password.py

from seguridad.password import es_password_valido


def test_password_es_valido_si_tiene_ocho_caracteres():
    assert es_password_valido("abcdefgh") is True


def test_password_es_valido_si_tiene_mas_de_ocho_caracteres():
    assert es_password_valido("abcdefghi") is True


def test_password_es_invalido_si_tiene_menos_de_ocho_caracteres():
    assert es_password_valido("abc") is False

Ejecutamos python -m pytest. Esta prueba debería pasar con la implementación actual.

5.13 Cuando una nueva prueba ya pasa

A veces agregamos una prueba nueva y ya pasa sin modificar el código. Eso puede ocurrir cuando la implementación existente ya cubre ese caso.

No es un problema, pero conviene preguntarse si la prueba aporta claridad. En este caso sí aporta, porque documenta que “al menos 8” incluye más de 8.

5.14 Posible refactor simple

Podemos extraer el número 8 a una constante para dar nombre a la regla.

Archivo a modificar: src/seguridad/password.py

LONGITUD_MINIMA = 8


def es_password_valido(password):
    return len(password) >= LONGITUD_MINIMA

Ejecutamos nuevamente python -m pytest. Si todo sigue en verde, el refactor es seguro.

5.15 Diferencia entre verde y refactor

La etapa verde busca que la prueba pase. La etapa refactor busca mejorar el código sin cambiar comportamiento.

Extraer LONGITUD_MINIMA no agrega una regla nueva. Solo da un nombre a un valor que ya existía en la implementación. Por eso pertenece a la etapa de refactorización.

5.16 Comportamiento que no debemos agregar todavía

Esta implementación acepta "12345678", "        " y "aaaaaaaa". Desde el requisito actual, todas esas cadenas tienen al menos 8 caracteres.

Si el negocio necesita rechazar espacios, exigir números o pedir mayúsculas, cada regla debe aparecer primero como una prueba roja nueva.

5.17 Errores frecuentes en la etapa verde

  • Implementar de más: agregar reglas no pedidas por la prueba actual.
  • No ejecutar la suite completa: una prueba nueva puede pasar y otra anterior puede romperse.
  • Refactorizar mientras hay pruebas rojas: primero hay que volver a verde.
  • Corregir la prueba para que pase: si la prueba expresa bien el requisito, el cambio debe estar en el código de producción.
  • Ignorar el fallo específico: cada error indica cuál es el siguiente paso mínimo.

5.18 Ejercicio propuesto

Agrega una nueva regla mediante TDD:

Una contraseña vacía debe ser inválida.

Primero escribe la prueba. Luego ejecuta python -m pytest. Si la prueba ya pasa por la regla de longitud mínima, decide si aporta claridad como documentación del caso especial. No agregues código nuevo si no hace falta.

5.19 Lista de verificación

Antes de continuar, verifica lo siguiente:

  • Creaste el módulo que la prueba intentaba importar.
  • Agregaste la función que la prueba necesitaba.
  • Escribiste primero una implementación mínima.
  • Generalizaste solo cuando una prueba lo exigió.
  • Ejecutaste python -m pytest después de cada cambio importante.
  • No agregaste reglas de validación no solicitadas por pruebas.
  • Comprendes la diferencia entre pasar la prueba y diseñar de más.

5.20 Conclusión

En este tema transformamos una prueba roja en una suite verde. Creamos el módulo faltante, agregamos la función, comenzamos con una implementación mínima y luego generalizamos solo cuando las pruebas lo justificaron.

En el próximo tema trabajaremos con la etapa de refactorización después de tener la barra verde, cuidando que las mejoras internas no cambien el comportamiento del programa.