De regreso a C++

Hace un par de semanas comencé a trabajar en proyecto con C++. Hacía ya bastante tiempo que estaba con ganas de hacer un proyecto de índole industrial/comercial con C++ por ello cuando me surgió la oportunidad de este proyecto, ni lo dudé a pesar de estar con una agenda casi completa.

El proyecto consiste básicamente en ayudar a un equipo a implementar prácticas técnicas para mejorar la calidad del producto. Dicho producto fue creado hace ya varios años y no cuenta con ningún tipo de pruebas (ni automatizadas ni manuales). El proceso de prueba es totalmente ad-hoc, lo cual implica que dependiendo quien realice la prueba, el resultado puede ser distinto.

Inicialmente pusimos en funcionamiento un servidor de integración continua (Jenkins). Luego revisamos el proceso del desarrollo-testing y propusimos algunos cambios y este momento nos encontramos trabajando en implementar “developer testing”: usando Google Test como framework de testing estamos escribiendo pruebas unitarias y de componentes sobre las partes más sensibles del producto.

Continuará…

 

Un ejemplo de la fragilidad de los tests de UI

Ayer me enfrenté a un claro ejemplo de la fragilidad de los tests de UI. Resulta que hace un tiempo empecé a trabajar en una aplicación sin ningún tipo de tests y cuyo diseño hacía muy difícil generar tests unitarios y de componentes/api. Fue por ello que una de las primeras cosas que hice fue generar un set de pruebas de UI para tener algo de cobertura sobre los 3 flujos más críticos de la aplicación. Luego de un deploy a testing de una nueva versión, un test de regresión comenzó a fallar. Curiosamente el nuevo deploy no incluía ningún cambio que impactara directamente en la funcionalidad que el test cubría. Para sumar confusión a la situación, cuando corría tests en mi máquina, todo andaba perfecto, el fallo sólo se producía cuando el tests era ejecutado desde el build server.

Luego de un buen rato de troubleshooting, descubrí que habíamos tenido un cambio menor en un hoja de estilo, ese cambio hacía que cuando los tests se corrían en una máquina con una determinada resolución, dos elementos quedarán superpuestos haciendo que uno de ellos no fuera clickeable.

ui-fragil

Automatización de test en proyecto “brown-field”

Hace poco más de un mes comencé a trabajar en un proyecto de desarrollo de una aplicación existente (brown-field project). La aplicación en cuestión era un monolito construido con una antigua versión de Grails y luego actualizado a Grails 2.5 y con no más de 30 pruebas unitarias. La visión del proyecto tenia 2 objetivos bien claros (sin ningún orden particular):

  • Realizar una rearquitectura de la aplicación para llevarla a un esquema de microservicios
  • Agregar un conjunto de funcionalidades relacionadas principalmente a cuestiones de integración con otras aplicaciones.

Una de mis primeras propuestas para el Product Owner fue comenzar generando un conjunto mínimo de pruebas end-2-end de regresión que nos dieran cierta seguridad para poder realizar modificaciones sobre la aplicación sin sonarla. El Product Owner siendo una persona de formación técnica estuvo de acuerdo con la propuesta y hacia allí fuimos. Comenzamos identificando las funcionalidades más críticas para el negocio y diseñando una arquitectura de pruebas para ellas.

Dado que la aplicación no había sido desarrollada con la “testeabilidad” en mente, resultaba muy dificil realizar pruebas que no unitarias y de API, asi que decidimos ir por un enfoque de caja negra realizando pruebas end-2-end que interactuaran con la aplicación via UI tal cual un usuario real.

Luego de un de par de pruebas de concepto llegamos a la siguiente arquitectura de tests:

test_arq

  • Gherkin: la idea de usar este DSL user-friendly no tiene un fundamento en que nos permite espeficar flujos funcionales desde una perspectiva de negocio.
  • Cucumber-JVM: como la aplicación está hecha en Grails el cual ya corre sobre JVM y los nuevos servicios se construirán en Java, nos pareio que Cucumber-JVM era la opción obvia. si es cierto que también podríamos haber usar JBehave, la realidad es que Cucumber-JVM está más difundido y por ello hay más documentación de soporte.
  • DB-Unit: lo utilizamos para cargar distintos set de datos para cada contexto de tests. Asimismo DBUnit se encarga de borrar los datos existentes antes de cargar cada dataset.
  • Selenium: era la opción default para interactuar con aplicaciones web. Para generar cierto nivel de abstracción sobre el webdriver utilizamos PageObjects.
  • JUnit: finalmente tenemos JUnit como librería de aserciones y motor de ejecución.

Continuará…

Automatización de pruebas en AS400, fin de la historia

Como mencioné tiempo atrás, en octubre comencé a trabajar en un proyecto para automatizar pruebas de una aplicación RPG/AS400. He completado mi participación en el proyecto, logramos dejar una arquitectura de pruebas funcionando con un par de casos automatizados, ahora está en manos del propio equipo de desarrollo continuar agregando nuevos casos.

El siguiente gráfico resume la arquitectura implementada:

test_as400

La arquitectura está armada de forma tal que un tester puede agregar fácilmente nuevos casos sin tener que meterse mucho en el código. Algo de código obviamente va a ser necesario escribir pero por la forma en que todo quedó armado (con template methods) es realmente poco lo que hay que codear.

Los casos de pruebas se generan con FitNesse y luego el gluecode/driver utiliza dos librerías de IBM: una para invocar funciones nativas en AS400 que se encargan de hacer el setup del ambiente con los datos iniciales y otra para la creación de las colas MQ y para leer/escribir en dichas colas.

Como la aplicación y los tests están codeados en distintas tecnología y almacenados en distintos repositorios, el servidor de integración continua (jenkins) quedó configurado para correr el set de tests ante cada commit y también en forma periódica 3 veces al día.

Estoy conforme con el resultado de nuestro trabajo, al mismo tiempo creo que fue un lindo desafío que me permitió aprender un poquito del mundo AS400. Ahora está en manos del codear más pruebas y sacar valor al trabajo realizado.

 

 

 

 

Reflexiones sobre la automatización de pruebas

La semana pasada dicté junto a @dfontde un taller de automatización de pruebas. Entre la audiencia tuvimos principalmente desarrolladores.

Obviamente uno de los temas que tratamos fue la automatización de pruebas a nivel de Interface Gráfica de Usuario (GUI). De hecho cada vez que dicto este taller la gente suele venir con la expectativa de encontrar la bala de plata para poder automatizar pruebas exclusivamente a nivel GUI. Mi posición al respecto es siempre la misma: ¿en verdad que quieres automatizar pruebas a ese nivel?.  A lo largo del taller trabajamos fuertemente sobre la cuestión de a qué nivel automatizar las pruebas y muchos suelen convencerse que puede resultarles más conveniente trabajar la automatización a nivel del API/Servicios.

Las pruebas a nivel GUI suelen ser frágiles (cambios mínimos en la GUI pueden romper las pruebas) y costosas de ejecutar (ya que requieren instanciar la aplicación completa). Pero al mismo tiempo pueden ser la única opción de automatización, ya que prueban la aplicación en modo caja negra, no importa como esté construida la aplicación pues simplemente se interactúa con ella desde afuera. Este es un punto muy relevante para el caso de aplicaciones legacy, que muchas veces han sido construidas sin características de testeabilidad. Más allá de estos argumentos que son clásicos y pueden encontrarse en la bibliografía hay algunas cuestiones adicionales que se han puesto de manifiesto en los últimos años.

  • Por un lado cada vez son más comunes las famosas SPAs (Single Page Application), muchas veces basadas en frameworks “client-side”como AngularJS. Esto implica una creciente cantidad de código JavaScript. Este tipo de aplicaciones no suele ser tan simple de automatizar a nivel GUI pues no todas las herramientas no se lo bancan bien. Pero al mismo tiempo aparece un nuevo nivel de automatización posible: pruebas unitarias de los componentes JavaScript.
  • Por otro lado, los frameworks de desarrollo / generadores de código “server-side” como Spring-Boot ya incluyen como parte de sus funcionalidades una propuesta para la codificación de pruebas automatizadas a nivel de API e incluso en algunos casos se encargan de generar el código de algunas de estas pruebas.

En mi opinión estas dos cuestiones refuerzan necesidad de repensar cualquier intento de automatización de pruebas a nivel GUI. Y quiero ser muy claro en cuanto a esto: no estoy diciendo que no se automaticen pruebas a nivel GUI, sino simplemente que no me parece una buena estrategia que el grueso de las pruebas automatizadas sea a nivel GUI. Creo que hay que pensar en una estrategia de automatización más integral que incluya pruebas a distintos niveles. Esto nos permitirá tener una mejor cobertura con un esfuerzo mucho menor y repartido entre los distintos perfiles del equipo (tengamos presente que el testing no es sólo responsabilidad de los testers).

Ejercicio: interpretación de métricas de test y cobertura

Amo las métricas, creo que es un efecto colateral de la búsqueda de la mejora continua. Para mejorar es necesario medir, pero con medir no basta, hay que saber qué medir y luego hay que interpretar lo medido para poder accionar al respecto.

Hoy quiero compartir un actividad que hicimos esta semana con mis alumnos de Ingeniería de software en UNTreF. La dinámica fue simple, dado un proyecto tomamos los gráficos de evolución de tests y cobertura generados por Jenkins y los analizamos para generar hipótesis a partir de ellos. Invito a los lectores a sumarse a este ejercicio, analicen los gráficos e intenten extraer información de ellos. Comparto un poco de contexto que les puede resultar útil para el análisis: el proyecto en cuestión es un modelo (o sea pura lógica, sin UI ni base de datos), los desarrolladores son alumnos universitarios que tenían que trabajar con 2 consignas:

  1. Hacer el desarrollo usando TDD
  2. Hacer integración commiteando al repositorio central (en el mainline) luego de cada nuevo test verde.
  3. Si el build se rompe, se para la línea hasta que sea arreglado

analisis_1

Aquí va mi análisis y con mis hipótesis/conclusiones (=>):

  • En el build #10 se agregaron 10 tests => no respetaron la consigna 2
  • Pero a pesar del agregado de tests la cobertura disminuyó => posiblemente por el agregado de código de dominio sin los correspondientes tests => violación de la consigna 1
  • En los siguientes builds (#11,#12 y #13) la cantidad de test aumenta de a uno y también se evidencia un aumento en la cobertura.
  • En los builds #14 y #15 se evidencian nuevamente saltos grandes en la cantidad de tests y de la mano de ello también un aumento de la cobertura.
  • Entre los builds #15 y #18 la cantidad de tests se mantiene constante y lo mismo ocurre con la cobertura => esto podría indicar pasos de refactoring.
  • En el build #19 disminuye la cantidad de tests y con ello la cobertura => esto podría deberse a un cambio en el código de dominio que no fue acompañado completamente por la correspondiente actualización de test.
  • Finalmente la cantidad de tests se mantiene constante en los últimos builds pero la cobertura disminuye => me hace pensar en agregado de código sin los correspondientes tests.

¿algo que agregar?

Automatización de pruebas en AS400

Hace un par de semanas comencé a trabajar un proyecto con @dfontde y @CodeRaguet para automatizar pruebas de una aplicación AS400/RPG. Nunca en mi mi vida había hecho nada con esa tecnología y creo que justamente ese fue un factor clave para que decida involucrarme en el proyecto.

Nuestro primer día de trabajo, uno de los programadores de la aplicación en cuestión nos hizo un breve tour introductorio a la plataforma AS400, al lenguaje RPG y a la aplicación sobre la cual debemos trabajar.

Dadas las particularidades de la plataforma y de cómo está construida la aplicación, decidimos tomar un enfoque de caja negra. Básicamente vamos a intercambiar mensajes con la aplicación via una cola. De esta forma nuestras pruebas se reducen a correr un setup (el cual hay que codear en RPG), escribir un mensaje en una cola, leer la respuesta de otra cola y verificar dicha respuesta. Todo esto lo vamos a codear en Java, que si bien no es “el sueño del pibe”, me resulta bastante más amistoso que RPG.

Hicimos una primer prueba de concepto exitosa para entender la interacción Java/PC <-> RPG/AS400. En este momento estamos analizando la herramienta de especificación de pruebas para lo cual estamos haciendo una prueba de concepto con FitNesse.

Continuará…