Joan Antoni Morey
5 min
27/06/2022
Un test es básicamente un mecanismo automatizado para comprobar si una función, caso de uso o funcionalidad sigue funcionando como se espera en el tiempo, aún cuando el código cambie. Su implementación varía dependiendo del lenguaje y objetivo. No es lo mismo simular una interacción de usuario en la cual varias cosas tienen que funcionar en armonía que verificar que una función devuelve siempre el mismo resultado recibiendo los mismos parámetros.
Así se ve un test en JavaScript:
function sum(a, b) {
return a + b;
}
describe("when passing the same two numbers to the sum function", () => {
it("should always return the same result", () => {
expect(sum(1, 2)).toEqual(3);
expect(sum(1, 2)).toEqual(3);
expect(sum(2, 4)).toEqual(6);
expect(sum(2, 4)).toEqual(6);
});
});
En esencia, una de las razones principales es la de añadir fiabilidad al proyecto. La otra razón es ganar confianza cuando subes tu código. Además, a través de los tests, todos los casos de uso quedan redactados y especificados. Con la ventaja de que cualquiera puede saber para qué existe ese código.
Quizás estés pensando que los tests conllevan un incremento del tiempo de desarrollo. Al menos es lo que parece visto desde fuera. La realidad es que escribir tests requiere invertir algo más de tiempo al principio, pero termina ahorrando mucho tiempo. Evita la mayoría de fallos (y el tiempo para corregirlos) y automatiza la comprobación de cada funcionalidad cuando se implementan cambios (quitando el factor humano como punto extra).
En otras palabras, se invierte el tiempo en prevenir la aparición y reaparición de fallos. Es más, piensa en la mala reputación que recae sobre el equipo de desarrollo cuando los fallos aparecen más de una vez.
Esto se puede evitar.
Como desarrollador, seguramente has escrito infinidad de funciones que tienen que lidiar con muchos casos de uso. Es fácil y manejable cuando empiezas una nueva funcionalidad de cero debido a la ausencia de casos de uso heredados. Estás en el punto de partida. Desarrollas el código necesario para satisfacer los casos de uso actuales y todo está correcto, no hay fuego en ninguna parte.
A medida que evoluciona la aplicación, es necesario controlar más casos de uso para tener una UX consistente. Es entonces cuando vuelves al código e intentas implementar la nueva lógica. Si el código fue escrito hace unas semanas, probablemente te acuerdes de por qué está escrito de esa forma en particular. Por qué hay un if-else devolviendo estructuras diferentes. Por qué es aparentemente más complejo de lo que debería, etc. Pero, ¿qué pasa si fue escrito hace meses?, ¿o hace un año?, o, todavía peor, ¿y si no lo escribiste tú? Ya no parece que la implementación de los nuevos casos de uso vaya a ser tan fácil.
¿Verdad?
Aún así das lo mejor de ti para, en primer lugar, recabar los casos de uso existentes y luego implementar los nuevos, satisfaciéndolos todos. Reproduces, a mano, cada caso que crees que es importante y compruebas que todo funciona correctamente. Al terminar, subes tu código para después desplegarlo a producción. Pero ahora te pregunto:
¿Alguna vez has tenido verdadera confianza al desplegar a producción? Deja que reformule la pregunta. ¿Sigues estando aterrorizado cuando haces un despliegue a producción?
Si te sientes identificado con la pregunta, déjame presentarte el paradigma de la programación segura: TESTING
Por supuesto no todos los casos de uso tienen que ser contemplados. Es más, resulta una pérdida de tiempo (y, al final, de dinero) intentar cubrirlos todos. Teniendo esto en cuenta, la filosofía debería ser:
Eso es todo. Cubre los principales casos de uso, no permitas que reporten los fallos más de una vez y ejecuta todos los tests a medida que introduces cambios.
Ahora estás en el camino de desarrollar con confianza y entregar confiabilidad.