9. Escribir nombres de pruebas que expliquen comportamiento

9.1 Objetivo del tema

En TDD, el nombre de una prueba no es un detalle menor. Una prueba debe decir qué comportamiento espera del sistema. Si el nombre es claro, la prueba funciona también como documentación ejecutable.

En este tema practicaremos cómo pasar de nombres genéricos a nombres que expliquen contexto, acción y resultado esperado.

Objetivo práctico: mejorar nombres de pruebas para que comuniquen comportamiento sin tener que leer toda la implementación.

9.2 Por qué importan los nombres

Cuando una prueba falla, pytest muestra el nombre de la prueba. Si el nombre es vago, el fallo aporta poca información. Si el nombre describe comportamiento, el fallo se entiende mucho más rápido.

Un buen nombre ayuda a responder tres preguntas:

  • ¿En qué situación estamos?
  • ¿Qué acción se realiza?
  • ¿Qué resultado se espera?

9.3 Ejemplo de nombre poco útil

Supongamos que tenemos una función para aplicar descuentos. Una prueba con este nombre no comunica demasiado:

Archivo de ejemplo: tests/test_descuentos.py

def test_descuento():
    assert aplicar_descuento(100, 10) == 90

Si falla, sabremos que algo relacionado con descuentos no funciona, pero no sabremos qué regla concreta se rompió.

9.4 Mejorar el nombre con comportamiento

Podemos escribir un nombre más específico:

Archivo a modificar: tests/test_descuentos.py

def test_aplicar_descuento_del_diez_por_ciento():
    assert aplicar_descuento(100, 10) == 90

Ahora el nombre comunica mejor qué se espera: aplicar un descuento del 10 por ciento.

9.5 Nombres orientados a reglas

Cuando la prueba expresa una regla de negocio, el nombre puede reflejar esa regla:

def test_precio_final_disminuye_segun_porcentaje_de_descuento():
    assert aplicar_descuento(100, 10) == 90

Este nombre es más general. Sirve cuando el ejemplo representa una regla, no solo un caso aislado.

9.6 Evitar nombres que repiten implementación

Este nombre habla demasiado de cómo podría estar programado el código:

def test_aplicar_descuento_usa_multiplicacion_y_resta():
    assert aplicar_descuento(100, 10) == 90

La prueba debería describir comportamiento, no obligar a usar una implementación determinada. Si mañana cambiamos la fórmula interna sin cambiar el resultado, la prueba no debería volverse confusa.

9.7 Convención práctica para nombrar

Una convención simple es usar nombres con esta idea:

test_[unidad]_[resultado esperado]_si_[condición]

Por ejemplo:

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

No es una regla obligatoria, pero ayuda a construir nombres expresivos.

9.8 Usar castellano en nombres de prueba

En Python es válido usar nombres de funciones en castellano sin acentos. Para pruebas de un curso en español, esto puede mejorar la comprensión del alumno.

def test_envio_es_gratis_si_total_es_cien_o_mas():
    assert calcular_envio(100) == 0

Conviene evitar acentos en identificadores Python para mantener compatibilidad y facilitar escritura en distintos teclados.

9.9 Nombres demasiado largos

Un nombre largo no siempre es mejor. Este nombre es preciso, pero pesado:

def test_la_funcion_calcular_envio_devuelve_cero_cuando_el_total_de_la_compra_es_mayor_o_igual_a_cien_pesos():
    assert calcular_envio(100) == 0

Podemos reducirlo sin perder la idea principal:

def test_envio_es_gratis_si_total_es_cien_o_mas():
    assert calcular_envio(100) == 0

9.10 Nombres demasiado cortos

El extremo opuesto también es problemático:

def test_1():
    assert calcular_envio(100) == 0

Este nombre no aporta información. Si falla, obliga a abrir el archivo y leer la prueba completa.

9.11 Nombres en pruebas parametrizadas

Cuando usamos parametrización, un solo nombre cubre varios ejemplos. Por eso debe describir la regla general:

import pytest


@pytest.mark.parametrize(
    "total, esperado",
    [
        (50, 10),
        (99, 10),
        (100, 0),
    ],
)
def test_calcular_envio_devuelve_costo_segun_total_de_compra(total, esperado):
    assert calcular_envio(total) == esperado

El nombre no menciona un caso específico, sino la regla que todos los casos verifican.

9.12 Identificadores para casos parametrizados

Podemos mejorar el reporte de pytest usando id en cada caso:

import pytest


@pytest.mark.parametrize(
    "total, esperado",
    [
        pytest.param(50, 10, id="compra-menor-a-cien"),
        pytest.param(99, 10, id="limite-inferior"),
        pytest.param(100, 0, id="envio-gratis"),
    ],
)
def test_calcular_envio_devuelve_costo_segun_total_de_compra(total, esperado):
    assert calcular_envio(total) == esperado

Si un caso falla, el identificador ayuda a ubicar rápidamente qué ejemplo falló.

9.13 Nombre de archivo y nombre de prueba

El archivo también comunica intención. Un archivo llamado test_envio.py agrupa pruebas de envío. Dentro de ese archivo, cada función debe precisar un comportamiento.

tests/
`-- test_envio.py

Evita archivos demasiado genéricos como test_utils.py si terminan mezclando reglas de negocio sin relación clara.

9.14 Nombres que acompañan el diseño

En TDD, si cuesta nombrar una prueba, puede ser señal de que el comportamiento no está claro. Antes de escribir código, conviene aclarar la regla.

Por ejemplo, este nombre revela una ambigüedad:

def test_descuento_es_correcto():
    assert aplicar_descuento(100, 10) == 90

¿Correcto según qué regla? ¿Descuento porcentual? ¿Descuento fijo? ¿Cupón? El nombre nos obliga a pensar mejor.

9.15 Refactorizar nombres de pruebas

Cambiar el nombre de una prueba es una refactorización. No cambia el comportamiento verificado, pero mejora la comunicación.

Después de renombrar, ejecutamos:

python -m pytest

Si todo sigue en verde, el cambio fue seguro.

9.16 Tabla de ejemplos

Comparación entre nombres poco útiles y nombres más expresivos:

  • test_descuento: demasiado genérico.
  • test_aplicar_descuento_del_diez_por_ciento: describe un caso concreto.
  • test_precio_final_disminuye_segun_porcentaje_de_descuento: describe una regla.
  • test_1: no comunica comportamiento.
  • test_envio_es_gratis_si_total_es_cien_o_mas: comunica condición y resultado.

9.17 Errores frecuentes

  • Usar nombres numéricos: test_1, test_2 no explican nada.
  • Nombrar según implementación: ata la prueba a detalles internos.
  • Usar nombres demasiado generales: dificultan diagnosticar fallos.
  • Escribir nombres enormes: pueden ser difíciles de leer y mantener.
  • No actualizar nombres después de refactorizar: la prueba puede quedar diciendo algo distinto de lo que realmente verifica.

9.18 Ejercicio guiado

Mejora los nombres de estas pruebas:

def test_envio():
    assert calcular_envio(100) == 0


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


def test_puntos():
    assert calcular_puntos(95) == 9

Una posible mejora sería:

def test_envio_es_gratis_si_total_es_cien_o_mas():
    assert calcular_envio(100) == 0


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


def test_puntos_ignoran_pesos_que_no_completan_decena():
    assert calcular_puntos(95) == 9

9.19 Ejercicio propuesto

Toma tres pruebas de temas anteriores y renómbralas para que el nombre explique mejor el comportamiento. Después ejecuta:

python -m pytest

El objetivo es que todos los tests sigan pasando y que el reporte de pytest sea más comprensible.

9.20 Lista de verificación

Antes de continuar, verifica lo siguiente:

  • El nombre de cada prueba describe comportamiento.
  • El nombre evita detalles internos de implementación.
  • El nombre no es tan genérico que obligue a leer todo el cuerpo.
  • El nombre no es innecesariamente largo.
  • Los casos parametrizados tienen una descripción general clara.
  • Ejecutaste python -m pytest después de renombrar.

9.21 Conclusión

En este tema vimos que el nombre de una prueba es parte de su valor. Una prueba bien nombrada ayuda a entender el requisito, diagnosticar fallos y usar la suite como documentación ejecutable.

En el próximo tema ordenaremos el cuerpo de las pruebas con el patrón Arrange, Act, Assert para que, además del nombre, la estructura interna también sea clara.