Pruebas y calidad de código

Qué hace buena a una prueba unitaria: resumen de los 5 principios FIRST

¿Has escrito muchísimo código de pruebas y, aun así, no has detectado los errores reales, mientras las pruebas se rompían una tras otra cada vez que corregías una función?

4 min de lectura
Imagen de portada de Qué hace buena a una prueba unitaria: resumen de los 5 principios FIRST

¿Has escrito muchísimo código de pruebas y, aun así, no has detectado los errores reales, mientras las pruebas se rompían una tras otra cada vez que corregías una función?

En resumen, las buenas pruebas unitarias no se juzgan por su cantidad ni por la cobertura, sino por si siguen los principios FIRST. FIRST corresponde a Fast (rápida), Isolated (aislada), Repeatable (repetible), Self-validating (autoverificable) y Timely (oportuna).

Si recuerdas estos cinco puntos, las pruebas dejan de ser una carga y se convierten en una red de seguridad fiable. Hoy los explicaré uno por uno, con ejemplos de mi experiencia.

Los principios FIRST de un vistazo

FIRST es un conjunto de principios popularizado por Robert Martin (Uncle Bob) en su libro Clean Code.

Cada letra contiene una condición de una buena prueba unitaria.

Letra Significado Clave
F Fast Cientos en pocos segundos
I Isolated Las pruebas no interfieren entre sí
R Repeatable El mismo resultado siempre y en cualquier lugar
S Self-validating Determina automáticamente si pasa o falla
T Timely Escrita en el momento adecuado

Vamos a analizarlos uno por uno.


Fast: ¿por qué deben ser tan rápidas?

Las pruebas unitarias deben ser rápidas. De verdad.

Si son lentas, los desarrolladores dejan de ejecutarlas. Un conjunto de pruebas que tarda 3 minutos por ejecución acaba ejecutándose apenas antes de hacer commit.

Las pruebas cumplen su función cuando puedes ejecutar cientos de ellas sin esfuerzo cada vez que modificas el código.

Las causas más habituales de lentitud son acceder a una DB real, a la red o al sistema de archivos. Lo habitual es sustituir estas dependencias externas por dobles de prueba (Mock, Stub).

Un buen objetivo es tardar unos milisegundos por prueba y terminar cientos de pruebas en pocos segundos en total.

Esa tranquilidad cuando todas las pruebas están en verde, ¿la conoces?
Esa tranquilidad cuando todas las pruebas están en verde, ¿la conoces?

Isolated: por qué las pruebas no deben interferirse

Cada prueba debe ser independiente. No debe depender del resultado de otra prueba.

Por ejemplo, si la prueba A crea datos que usa la prueba B, cuando A falla, B también se viene abajo. Además, el resultado cambia si cambia el orden de ejecución.

Por eso cada prueba debe preparar los datos que necesita y limpiarlos correctamente al terminar.

Este es un patrón habitual para restablecer el estado antes de cada prueba.

struct CalculatorTests {
    let calculator: Calculator

    init() {
        // Se crea una instancia nueva para cada prueba → evita compartir estado suite 
        calculator = Calculator()
    }
}

Así, ninguna prueba afecta a las demás, independientemente de cuál se ejecute primero.

Suite de pruebas de Swift Testing escrita con @Test e init() en Xcode, con la pantalla de resultados satisfactorios
Gracias a init(), cada prueba comienza con una instancia nueva y permanece aislada

Repeatable & Self-validating: los resultados no deben fluctuar

Empecemos por R, la repetibilidad. Una prueba debe producir el mismo resultado sin importar cuántas veces ni en qué entorno se ejecute.

Una prueba que pasa hoy pero falla mañana se llama prueba flaky. Es una de las principales causas de pérdida de confianza.

Depender de la hora actual o de valores aleatorios facilita este problema. Si necesitas trabajar con el tiempo, es más seguro controlarlo inyectando un valor fijo.

S, la autoverificación, también es importante. La prueba debe determinar por sí misma si pasa o falla.

Imprimir un valor en la consola y comprobarlo visualmente no es una prueba. Debes usar aserciones como #expect para determinar el resultado automáticamente.

Una buena prueba solo muestra una de dos cosas: verde o rojo. Si hace que alguien se pregunte «¿esto es correcto?», la prueba ya ha fallado.


Timely: ¿cuándo conviene escribir las pruebas?

La última T representa la oportunidad: escribir las pruebas en el momento adecuado.

En TDD (Test-Driven Development, desarrollo dirigido por pruebas), las pruebas se escriben antes que el código de producción. Aunque no sigas exactamente ese enfoque, lo importante es escribirlas cerca del momento de implementar la función.

Si esperas varias semanas después de terminar una función para escribir todas las pruebas, a menudo el código ya se ha consolidado en una estructura difícil de probar.

No posponer las pruebas: ese es el núcleo de Timely.


Resumen

FIRST no es tanto una serie de reglas que memorizar como una lista de comprobación que ayuda a detectar qué se ha desviado cuando las pruebas resultan frustrantes.

¿Es lenta? ¿Están acopladas? ¿Fluctúan los resultados? ¿Requieren revisión manual? ¿Las escribes demasiado tarde? Con solo comprobar estos cinco puntos, la calidad de las pruebas mejora notablemente.

Desde hoy, recuerda FIRST cada vez que escribas una prueba. Sin duda se convertirá en un compañero fiable.

Seguir leyendo