Mediados de octubre, un día otoñal. Fuera llueve y das las gracias por trabajar en remoto. Música en los auriculares para concentrarte. Los dedos golpeando el teclado.
De repente, la notificación de un correo desvía la atención del código que, curiosamente, tenía algo que no encajaba: otra pull request incluida en la rama principal.
Pero esta tenía algo especial. La pipeline de despliegue había ejecutado con éxito 1000 tests. Mensaje al equipo. Bandeja de bollos para celebrar el acontecimiento 🥳.
Así transcurrió, más o menos, la mañana en la que en Aton nos dimos cuenta de que habíamos escrito mil tests en la code base del backend. Fue un pequeño pero significativo hito, posible gracias a un recorrido y a un cambio de mentalidad que intentaré contaros en este artículo.
¡Empecemos!
Los tests y el TDD (Test Driven Development) son temas bastante extendidos dentro de la comunidad tech, y la literatura al respecto es realmente abundante. Simplificando al máximo la cuestión, escribir tests significa escribir código que verifique y cristalice el comportamiento de otro código.
Verificar el comportamiento significa comprobar que, dado un cierto input, siempre se produzca el mismo output. Tanto en los casos más simples y lineales como en los más complejos y sutiles. Además, permite constatar si el comportamiento de lo escrito se ajusta a las especificaciones definidas en la fase de análisis.
Cristalizar, en cambio, cobra mayor importancia en términos de mantenibilidad de una code base. Esta, de hecho, evoluciona con el tiempo y es utilizada por varias personas simultáneamente, que pueden modificar lógicas comunes o heredadas. Tener tests que den fe de lo realizado en el pasado garantiza al desarrollador la seguridad de que lo que ha implementado no rompa la compatibilidad con lo ya existente.
¡Todo esto no está exento de esfuerzo! Escribir tests también significa alargar el tiempo de desarrollo para poder incluir las actividades relacionadas con el setup de los propios tests (por ejemplo, datos de mockup, carga de la base de datos, parámetros de configuración, etc.), con su escritura propiamente dicha y con el fix del comportamiento de la lógica de negocio cuando, inevitablemente, algún test falla.
Al mismo tiempo, esta ampliación del desarrollo se compensa cuando el código no falla estrepitosamente en producción y ahorra al desarrollador unas cuantas noches en vela de debug.
Vale, la teoría está muy bien. Pero en Aton, ¿cómo se escriben los tests?
Desde que en Aton empezamos a desarrollar las nuevas líneas de producto basadas en stacks tecnológicos modernos (Java/Kotlin y Spring Boot para el backend y Angular 2 para el frontend), escribir tests se ha convertido en una actividad cotidiana para los desarrolladores.
Intentamos, en la medida de lo posible, seguir el TDD: dada una funcionalidad, primero se escriben los tests y solo después la lógica de negocio relativa a esa funcionalidad. Una vez finalizado el desarrollo, se lanzan los tests y se comprueba si el comportamiento definido inicialmente se respeta en la implementación.
Si todos los tests pasan, entonces se puede considerar que la funcionalidad está completada.
De lo contrario, cuando algún test falla, las posibilidades son dos:
1. La implementación no se ajusta a las especificaciones. Por ejemplo, no gestiona ciertos corner cases, o devuelve un conjunto de datos incoherente con lo esperado.
2. La definición de los tests es errónea, es decir, se han definido tests que no se ajustan a las especificaciones o con datos de input que no corresponden con el output esperado.
Independientemente de cuál sea la razón que haya hecho fallar los tests, el desarrollador debe volver a tocar el código para asegurarse de que todos los tests relacionados con la funcionalidad en desarrollo pasen en verde. Cuando esto ocurre, el trabajo puede considerarse completado y el desarrollador puede abrir una pull request con lo realizado.
Es en este momento cuando entra en juego la segunda fase del proceso de desarrollo del backend.
La creación de una pull request desencadena una pipeline en Azure DevOps que, de manera diligente, ejecuta los tests de la nueva funcionalidad y de todas las funcionalidades anteriores. Esta operación actúa en el plano de la cristalización: se comprueba que la nueva lógica no entre en conflicto con la ya existente.
Si algo se rompe, la pipeline se detiene e impide la integración en la rama principal.
La escritura de tests es una actividad que ha aportado un gran valor a Aton, y por ello pensamos seguir apoyándola en el tiempo.
Sin embargo, no está dicho que la forma en la que escribamos los tests vaya a ser siempre la misma.
El tema de las inteligencias artificiales generativas y de cómo estas pueden convertirse en herramientas de apoyo para los desarrolladores (y no solo), mejorando su productividad, es muy debatido y relevante en la comunidad tech.
Entre todas las herramientas actualmente disponibles, GitHub Copilot es sin duda la más útil para quienes escriben código de forma profesional, también en la escritura de tests. De hecho, proporcionando el contexto adecuado, es posible hacer que GitHub Copilot genere unit tests (y otros tipos) para una funcionalidad determinada.
Utilizar este enfoque tiene una serie de pros y contras:
👍🏻 Tiempos de desarrollo más cortos. El desarrollador dedica menos tiempo a la escritura de tests y más tiempo a la lógica de negocio;
👍🏻 Casos de test no previstos. La inteligencia artificial puede generar casos de test que el desarrollador no había contemplado, aumentando así la solidez de la code base;
👍🏻 Casos de test imparciales. El desarrollador tiende a ser “cariñoso” con su código y por ello propenso a escribir tests conservadores que no lo ponen realmente a prueba (esto es aún más marcado cuando quien escribe los tests y quien implementa son la misma persona);
👎🏻Falsa sensación de seguridad. Los tests generados por la inteligencia artificial deben ser siempre revisados por el desarrollador para corregir posibles interpretaciones erróneas o contextos demasiado vagos para la IA.
Si este será el camino definitivo para el testing, a día de hoy no lo sabemos. Sin embargo, en Aton se han puesto en marcha actividades de investigación y desarrollo precisamente sobre GitHub Copilot para entender qué impacto puede tener en el daily life de un desarrollador.
Si al principio del artículo os preguntabais por qué celebramos tanto la llegada del test número mil, ahora debería estar un poco más claro.
Mil tests no son solo una estadística, sino el testimonio de un recorrido que ha cambiado nuestra forma de trabajar. Un recorrido que está industrializando poco a poco el proceso de desarrollo en Aton y que, también gracias a los tests, consigue producir software de calidad.
Si todavía te preguntas si merece la pena escribir tests, en Aton podemos responderte que sí.
Merece la pena porque cambia tu forma de trabajar. Merece la pena porque cambia tu forma de pensar. Merece la pena porque aumenta la calidad del código que escribes. Merece la pena porque te da la seguridad de que lo que has escrito no rompe lo que ya estaba hecho. Merece la pena porque, al fin y al cabo, ver todas esas marcas verdes cuando pasan los tests provoca un subidón de dopamina nada desdeñable.
¿Quieres trabajar con nosotros?