32. Ritmo de trabajo: commits pequeños, pruebas rápidas y retroalimentación constante

32.1 Objetivo del tema

En este tema veremos cómo sostener un ritmo de trabajo práctico con TDD. No alcanza con conocer el ciclo rojo, verde y refactor: también necesitamos ejecutar pruebas con frecuencia, mantener cambios pequeños y registrar avances en commits claros.

El objetivo es trabajar de forma que cada paso sea verificable, reversible y fácil de revisar.

32.2 El ritmo básico de TDD

Un ciclo sano suele verse así:

  1. Escribir una prueba pequeña que falle por la razón esperada.
  2. Implementar el mínimo código para pasarla.
  3. Ejecutar las pruebas relevantes.
  4. Refactorizar si hay una mejora clara.
  5. Ejecutar nuevamente las pruebas.
  6. Hacer un commit pequeño cuando el sistema queda en verde.
Un buen ritmo reduce el costo de equivocarse. Si algo falla, sabemos qué cambio reciente lo produjo.

32.3 Caso práctico

Trabajaremos con una función para calcular puntos de fidelidad. La regla inicial será:

Por cada 10 unidades monetarias de compra, el cliente recibe 1 punto.

Usaremos este ejemplo para mostrar cuándo ejecutar pruebas y cuándo conviene crear un commit.

32.4 Primera prueba

Comenzamos con un caso simple.

Archivo a crear: tests/test_fidelidad.py

from fidelidad import calcular_puntos


def test_calcula_un_punto_por_cada_diez_unidades():
    puntos = calcular_puntos(total_compra=50)

    assert puntos == 5

Ejecutamos la prueba.

python -m pytest tests/test_fidelidad.py

En esta etapa esperamos rojo: el módulo o la función todavía no existen.

32.5 Implementación mínima

Creamos el código necesario.

Archivo a crear: src/fidelidad.py

def calcular_puntos(total_compra):
    return total_compra // 10

Ejecutamos otra vez:

python -m pytest tests/test_fidelidad.py

Si pasa, estamos en verde para este comportamiento.

32.6 Primer commit pequeño

Si el cambio es coherente y la prueba pasa, puede ser momento de guardar el avance.

git status
git add src/fidelidad.py tests/test_fidelidad.py
git commit -m "Agrega cálculo básico de puntos de fidelidad"

El commit describe una unidad de comportamiento, no una sesión entera de trabajo.

32.7 Segunda prueba: redondeo hacia abajo

Agregamos un borde para confirmar que una compra de 59 genera 5 puntos, no 6.

Archivo a modificar: tests/test_fidelidad.py

def test_puntos_redondean_hacia_abajo():
    puntos = calcular_puntos(total_compra=59)

    assert puntos == 5

Ejecutamos solo el archivo de pruebas del tema actual:

python -m pytest tests/test_fidelidad.py

Esta prueba probablemente ya pase. Aun así, documenta una decisión importante.

32.8 Cuándo ejecutar una prueba específica

Durante el ciclo corto conviene ejecutar primero la prueba o el archivo relacionado con el cambio actual.

python -m pytest tests/test_fidelidad.py::test_puntos_redondean_hacia_abajo

Esto da retroalimentación rápida. Luego, antes de cerrar un cambio, ejecutamos una suite más amplia.

32.9 Tercera prueba: total inválido

Agregamos una validación.

Archivo a modificar: tests/test_fidelidad.py

import pytest


def test_total_compra_no_puede_ser_negativo():
    with pytest.raises(ValueError, match="El total no puede ser negativo"):
        calcular_puntos(total_compra=-10)

Esta prueba debe fallar con la implementación actual.

32.10 Implementar validación

Agregamos el mínimo código para pasar la nueva prueba.

Archivo a modificar: src/fidelidad.py

def calcular_puntos(total_compra):
    if total_compra < 0:
        raise ValueError("El total no puede ser negativo")

    return total_compra // 10

Ejecutamos primero la prueba específica y luego el archivo completo.

python -m pytest tests/test_fidelidad.py::test_total_compra_no_puede_ser_negativo
python -m pytest tests/test_fidelidad.py

32.11 Cuándo ejecutar toda la suite

No siempre hace falta ejecutar todo después de cada tecla, pero sí conviene hacerlo en momentos claros.

  • Antes de hacer un commit.
  • Después de un refactor.
  • Después de tocar código compartido.
  • Antes de cambiar de tarea.
  • Antes de subir cambios a un repositorio remoto.
python -m pytest

32.12 Refactor pequeño

Si aparece una constante de negocio, podemos darle nombre.

Archivo a modificar: src/fidelidad.py

UNIDADES_POR_PUNTO = 10


def calcular_puntos(total_compra):
    if total_compra < 0:
        raise ValueError("El total no puede ser negativo")

    return total_compra // UNIDADES_POR_PUNTO

Después del refactor ejecutamos:

python -m pytest tests/test_fidelidad.py
python -m pytest

32.13 Segundo commit pequeño

Ahora el cambio incluye validación y una mejora de nombres. Si la suite está en verde, el commit puede ser:

git status
git add src/fidelidad.py tests/test_fidelidad.py
git commit -m "Valida total negativo en cálculo de puntos"

Si el refactor fue pequeño y relacionado, puede quedar en el mismo commit. Si fue más amplio, conviene separarlo.

32.14 Qué hace bueno a un commit

  • Representa una unidad de comportamiento o refactor claro.
  • La suite relevante pasa antes de crearlo.
  • El mensaje explica la intención del cambio.
  • No mezcla tareas sin relación.
  • Permite volver atrás sin perder demasiado trabajo.

32.15 Qué evitar en los commits

  • Commits enormes con muchas reglas nuevas.
  • Commits que mezclan formato, refactor y comportamiento sin necesidad.
  • Commits con pruebas rotas.
  • Mensajes genéricos como cambios o fix.
  • Subir código que solo funciona en la máquina local.

32.16 Usar pytest para acelerar el ciclo

pytest permite ejecutar subconjuntos de pruebas.

python -m pytest tests/test_fidelidad.py
python -m pytest tests/test_fidelidad.py -q
python -m pytest -k "fidelidad"
python -m pytest -x

-q reduce salida, -k filtra por nombre y -x detiene la ejecución en el primer fallo.

32.17 Retroalimentación constante

La retroalimentación no viene solo de las pruebas. También puede venir de:

  • El nombre de una prueba que cuesta escribir.
  • Una prueba roja que falla por una razón inesperada.
  • Un refactor que rompe demasiadas pruebas.
  • Un commit difícil de describir.
  • Una suite lenta que desalienta la ejecución frecuente.

Cada señal dice algo sobre el diseño o sobre el ritmo de trabajo.

32.18 Separar ciclos cortos y validación amplia

Un flujo práctico puede dividirse así:

Momento Acción
Mientras escribo código Ejecutar la prueba específica
Después de pasar a verde Ejecutar el archivo de pruebas relacionado
Después de refactorizar Ejecutar la suite completa
Antes de commit Revisar cambios y ejecutar pruebas relevantes

32.19 Revisar antes de commitear

Antes de crear un commit, conviene revisar qué cambió.

git status
git diff

La revisión ayuda a detectar cambios accidentales, código de depuración o archivos que no deberían incluirse.

32.20 Ejemplo de sesión completa

python -m pytest tests/test_fidelidad.py
# rojo

# implementar mínimo
python -m pytest tests/test_fidelidad.py
# verde

# refactor pequeño
python -m pytest
# verde

git status
git diff
git add src/fidelidad.py tests/test_fidelidad.py
git commit -m "Agrega regla de puntos de fidelidad"

El objetivo es que cada sesión deje el sistema en un estado entendible.

32.21 Señales de buen ritmo

  • Las pruebas se ejecutan muchas veces durante el desarrollo.
  • Los fallos se entienden rápido.
  • Los commits son pequeños y tienen intención clara.
  • Los refactors ocurren con la suite en verde.
  • Es fácil explicar qué cambió desde el commit anterior.

32.22 Señales de mal ritmo

  • Hay demasiados archivos modificados sin commit.
  • Se ejecutan pruebas solo al final del día.
  • Un fallo obliga a revisar muchos cambios juntos.
  • Los commits mezclan varias historias o tareas.
  • La suite completa tarda tanto que se evita ejecutarla.

32.23 Ejercicio práctico

Practicá una sesión completa de TDD con una función calcular_nivel_cliente.

  1. Escribí una prueba para cliente común con menos de 100 puntos.
  2. Implementá el mínimo código.
  3. Ejecutá la prueba específica.
  4. Agregá una prueba para cliente frecuente desde 100 puntos.
  5. Ejecutá la suite completa y prepará un commit pequeño.

32.24 Checklist del tema

  • El ciclo corto usa pruebas específicas para obtener respuesta rápida.
  • La suite completa se ejecuta antes de cerrar cambios importantes.
  • Los commits guardan unidades pequeñas de comportamiento o refactor.
  • El mensaje del commit describe intención, no solo actividad.
  • La retroalimentación constante guía tanto el código como el ritmo de trabajo.

32.25 Conclusión

TDD funciona mejor cuando se practica con un ritmo sostenido: pruebas pequeñas, ejecución frecuente, refactors controlados y commits claros. Esa disciplina reduce el riesgo de cada cambio y facilita corregir el rumbo cuando algo falla.

En el próximo tema veremos cómo trabajar con TDD en código existente usando caracterización antes de cambiar comportamiento.