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.
Partimos de las pruebas escritas en el tema anterior. El archivo de pruebas expresa dos comportamientos:
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
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
Agrega una nueva regla mediante TDD:
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.
Antes de continuar, verifica lo siguiente:
python -m pytest después de cada cambio importante.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.