Una prueba unitaria es un pedazo de código que ejecuta otro pedazo de código y comprueba que el resultado es el que esperabas. Nada más. Tomas una función, le das una entrada conocida, y verificas que devuelve la salida correcta. Si coincide, la prueba pasa. Si no, falla y te avisa antes de que lo haga un usuario.
Esa es la respuesta corta. La respuesta útil es entender por qué esta prueba, la más pequeña de todas, es la base sobre la que se apoya todo lo demás. Se llama "unitaria" porque examina una unidad: la pieza más pequeña de tu programa que tiene sentido probar por separado, casi siempre una función o un método.
01 · la baseQué es una prueba unitaria y qué la hace "unidad"
Piensa en la pirámide de pruebas, un modelo que ordena los tipos de test por cantidad y velocidad. En la base van muchas pruebas unitarias: rápidas, baratas, aisladas. En el medio, menos pruebas de integración, que revisan que varias piezas encajan. Y en la punta, pocas pruebas de extremo a extremo, que recorren el sistema completo como lo haría una persona. La forma de pirámide no es decorativa: dice cuántas pruebas de cada tipo conviene tener. Mario Fowler popularizó esta figura a partir del trabajo de Mike Cohn [1].
Lo que hace "unidad" a una prueba unitaria es el aislamiento. Prueba una sola cosa, sin depender de la base de datos, sin llamar a la red, sin tocar el reloj del sistema ni leer un archivo. Si tu prueba necesita todo eso para correr, ya no es unitaria: es de integración, y pertenece a otro nivel de la pirámide.
Si no puedes probar una función sin encender media aplicación, el problema no es la prueba: es el diseño de la función.
Esa restricción tiene un efecto secundario valioso. Escribir la prueba te obliga a razonar sobre qué recibe la función, qué devuelve y de qué depende. Una función difícil de probar suele ser una función que hace demasiado, o que esconde dependencias. La prueba unitaria es, sin quererlo, un detector de mal diseño.
02 · el patrónArrange, act, assert: la anatomía de toda prueba
Casi toda prueba unitaria bien escrita tiene tres partes, siempre en el mismo orden. En inglés se conoce como AAA: arrange, act, assert. En español: preparar, actuar, afirmar.
Preparar (arrange). Montas el escenario: creas los datos de entrada y cualquier objeto que la función necesite. Actuar (act). Llamas a la función que quieres probar, una sola vez, con esos datos. Afirmar (assert). Comparas lo que devolvió con lo que esperabas. Si no coincide, la prueba falla.
Veamos el ejemplo mínimo. Supón una función que suma dos números. La prueba se lee casi como una frase.
Ese assert es el corazón. Una afirmación que dice, sin ambigüedad, qué tiene que ser verdad. Cuando la función se rompa en el futuro, porque alguien la tocó sin querer, esa línea será la que encienda la alarma. Una buena prueba unitaria tiene idealmente una afirmación clara: prueba una cosa y da una razón concreta cuando falla.
El error de principiante es escribir una prueba que pasa siempre, incluso con la función rota. Antes de confiar en una prueba, rómpela a propósito: cambia la función para que dé un resultado incorrecto y confirma que la prueba falla. Si sigue pasando en verde con la función averiada, tu prueba está mirando al lado equivocado.
03 · el valorPor qué son rápidas, baratas y la primera línea de defensa
Una prueba unitaria corre en milisegundos porque no toca nada externo. Puedes tener miles y ejecutarlas todas en segundos, cada vez que guardas un cambio. Ese ciclo cortísimo es lo que las hace baratas: el costo de escribir una es bajo, y el costo de ejecutarla es casi cero. Kent Beck construyó el desarrollo guiado por pruebas justo sobre esa idea, escribir la prueba antes que el código para que el diseño nazca ya verificado [2].
El valor no está en una prueba, sino en tenerlas todas corriendo juntas. Cuando cambias una función y sin querer rompes otra a diez archivos de distancia, la prueba de esa otra función falla de inmediato. Te enteras en segundos, no en producción. Eso es una red de seguridad, y es lo que permite modificar código con confianza en lugar de con miedo.
Sin pruebas unitarias, cada cambio es una apuesta. Con ellas, es una hipótesis que se verifica al instante.
La ISTQB, el organismo internacional de certificación de testing, ubica la prueba unitaria como el primer nivel de prueba, el más cercano al código y el que ejecutan quienes lo escriben [3]. No es casualidad que sea el primero: cuanto antes se atrapa un defecto, más barato sale corregirlo. Un fallo detectado por una prueba unitaria cuesta minutos; el mismo fallo descubierto por un cliente cuesta reputación.
Hay un límite honesto que conviene decir. Las pruebas unitarias comprueban que cada pieza funciona por separado, pero no que el sistema completo funciona junto. Puedes tener cien funciones perfectas que, ensambladas, hacen algo incorrecto. Para eso están los otros niveles de la pirámide. La prueba unitaria no es toda la calidad: es su base. Y una base sólida es la condición para que todo lo que va encima se sostenga.
Empieza por lo pequeño. Toma la función más simple que tengas, una que reciba algo y devuelva algo, y escríbele una prueba con los tres actos: prepara una entrada, llámala, afirma la salida. Rómpela para confirmar que la prueba avisa. Esa primera prueba en verde, que corre en un parpadeo, es la unidad más pequeña de confianza que puedes construir sobre tu código. Todo lo demás se apoya ahí.
Fuentes
- Fowler, M. (2012). Test Pyramid. martinfowler.com/bliki/TestPyramid.html. (Populariza la figura a partir de Cohn, M., Succeeding with Agile, Addison-Wesley, 2009.)
- Beck, K. (2002). Test-Driven Development: By Example. Addison-Wesley Professional. ISBN 978-0321146533.
- ISTQB (International Software Testing Qualifications Board). Certified Tester Foundation Level Syllabus, sección sobre niveles de prueba (component/unit testing). istqb.org.
- ISO/IEC/IEEE 29119-4. Software and systems engineering. Software testing. Part 4: Test techniques. International Organization for Standardization.