Cuando una empresa no le da la misma importancia a escribir pruebas unitarias que al desarrollo, tiende a crear proyectos muy poco mantenibles. Escribir pruebas unitarias efectivas, aunque lleve algo de tiempo, es clave a la hora de crear código limpio y mantenible. Crear un buen banco de pruebas ayudará a que el código tenga menos errores y sea más fácil de evolucionar.
Es por eso que se le debe dar siempre la misma importancia a la creación de pruebas unitarias que al código que se implementa. Para ello, debemos tener claro qué son y qué beneficios nos traen.
¿Qué son las pruebas unitarias?
Las pruebas unitarias se encargan de probar componentes individuales del software para garantizar que funcionen según lo previsto. Esto hace que tengamos que dividir el código en unidades pequeñas y comprobables para poder probarlas individualmente. Cuando dividimos el código en pequeñas unidades, permitimos crear pruebas unitarias más eficientes y efectivas, lo que nos permite localizar y corregir errores rápidamente en las primeras etapas del desarrollo.
El objetivo final de las pruebas unitarias es validar y comparar el comportamiento real del software con el comportamiento esperado. Estas pruebas no dependen de servicios externos, lo que las hace más rápidas a la hora de ejecutarse. En definitiva, proporcionan a los desarrolladores un entorno confiable donde la eficiencia, la productividad y la calidad del código son primordiales
Características de una buena prueba unitaria
Las pruebas unitarias son una parte esencial en el proceso de desarrollo, es por eso que debemos conocer qué debe comprender una buena prueba unitaria. Las siguientes son las características de una prueba unitaria efectiva:
- Automatizable: la ejecución de las pruebas unitarias debe ser automatizable, de esta manera facilitamos la comprobación de nuestro código sin tener que ir uno por uno.
- Completas: las pruebas unitarias deben cubrir la mayor cantidad de código posible. Se recomienda mantener una cobertura de al rededor del 60 – 80%.
- Rápidas: las pruebas unitarias se ejecutan en fracciones de segundo.
- Independientes: las pruebas unitarias deben ejecutarse de forma aislada, sin interdependencias o dependencias externas (archivos, bases de datos, etc.).
- Confiables: las pruebas unitarias solo deberían fallar si hay un error en el código subyacente.
Es posible que no todos los proyectos puedan ejecutar las pruebas de la misma manera, pero al menos se debe asegurar el cumplimiento de estas características a la hora de crearlas.
10 Claves para crear pruebas unitarias efectivas
Para poder crear unas pruebas unitarias efectivas y de gran calidad, deberíamos seguir las mejores prácticas a la hora de escribirlas. Las siguientes son algunas de las mejores prácticas a tener en cuenta al escribir pruebas unitarias:
1. Escribe Pruebas Fáciles De Leer
La legibilidad de las pruebas marcarán la diferencia a la hora de comprobar los errores. Si una prueba revela la lógica de ejecución de manera concisa, no debería hacer falta depurar el código a la hora de comprobar el fallo. Esta legibilidad también mejora la mantenibilidad de las pruebas, ya que los cambios en el código también deben actualizarse en las pruebas. Además, las pruebas con peor legibilidad tienden a crear malentendidos entre los desarrolladores, generando posibles fallos.
Maneras de escribir pruebas fáciles de leer:
- Mantén una convención de nombres para cada test. Los nombres de las pruebas pueden escribirse de varias maneras. Lo ideal es encontrar una única manera y escribir todos del mismo modo, ya sea utilizando camelCase, kebab-case, snake_case, etc.
- Crea bloques diferenciales utilizando la sintaxis Gherkin a la hora de crear las pruebas:
- Given: bloque donde se establece el caso de uso a probar
- When: ejecución del caso de uso a probar
- Then: comprobación del resultado
2. Evita Dependencias Entre Pruebas
Los ejecutores de pruebas generalmente ejecutan múltiples pruebas unitarias simultáneamente sin ceñirse a ningún orden en particular, por lo que las interdependencias entre las pruebas las hacen inestables y difíciles de ejecutar y depurar.
Para escribir pruebas independientes, no se debe asumir un orden. Si las pruebas están acopladas, se debería aislar el código en pequeños grupos/clases para probarlo de manera independiente. De lo contrario, los cambios realizados en una prueba pueden afectar a otras pruebas.
3. Procura Comprobar Un Caso Por Prueba
Las pruebas unitarias deben ser lo más simples posibles, es por ello que lo ideal debería ser que prueben una y solamente una cosa. Si la prueba unitaria realiza más de una comprobación se debería intentar refactorizar la prueba o en su defecto el caso de uso que se está probando.
4. Prueba Posibles edge cases
Los «edge cases» son casos que ocurren en los límites de los datos de entrada y, a menudo, son los más críticos de probar. Por ejemplo, a la hora de probar el login de un usuario, se deberían probar los casos de uso en los que el usuario se equivoque de la contraseña y del nombre de usuario.
Con estas pruebas, hacemos que el banco de pruebas no ejecute solamente la asunción básica del funcionamiento del código. De esta manera se consigue que el código sea más sólido y pueda manejar entradas inesperadas.
5. Evita Los magic numbers y Los magic Strings
Las pruebas deben ser claras y concisas. Añadir valores numéricos o cadenas de caracteres «hardcodeadas» pueden dificultar la lectura de las pruebas. Además, si es necesario cambiar el valor de alguna comprobación, es más sencillo crear una constante y modificarla una única vez.
6. Utiliza Test Driven Development (TDD)
El TDD es un proceso de desarrollo de software en el que las pruebas se escriben antes del código real. Sigue el principio de «red, green, refactor», donde el desarrollador primero escribe una prueba fallida (red), luego escribe el código necesario para pasar la prueba (green) y finalmente refactoriza el código para mejorar su diseño.
7. Ayúdate De Un TestRunner
Un «TestRunner» o ejecutor de pruebas, es una herramienta que automatiza la ejecución de las pruebas unitarias. Puede ejecutar todas las pruebas a la vez o puede ejecutar pruebas específicas según ciertos criterios. Los ejecutores de pruebas también proporcionan herramientas para realizar informes basados en los resultados y pueden ayudar a entender mejor el banco de pruebas.
8. Mantén Las Pruebas Lo Más Simples Posible
Las pruebas deben ser simples y fáciles de entender. Es mejor evitar lógicas complejas y bucles en ellas, ya que esto puede afectar a la comprensión del propósito de la prueba, y tener que depurar en caso de que falle. Además, como ya hemos indicado anteriormente, las pruebas deben cubrir un único escenario y comportamiento al mismo tiempo.
9. Automatiza Las Pruebas
Automatizar las pruebas es esencial si se quiere mantener una alta calidad en el código. La automatización ahorra tiempo y esfuerzo ya que comprueba que nuevos cambios no introduzcan bugs en el código.
10. Utiliza Librerías De Comprobación (Assert Libraries)
Las librerías de comprobación proporcionan una gran cantidad de funciones que ayudan a realizar un código más limpio y efectivo a la hora de escribir pruebas unitarias. Existen varias librerías de este tipo por ejemplo: Mockito o AssertJ.
En conclusión, las pruebas unitarias son una parte esencial del proceso de desarrollo de software y es importante seguir las mejores prácticas para garantizar conseguir pruebas unitarias efectivas, prácticas y eficientes.