Lecciones aprendidas automatizando pruebas en ambientes corporativos (parte 1)

En los últimos años he participado en un par de iniciativas para automatizar pruebas en «ambientes corporativos». Cuando digo ambientes corporativos me refiero a organizaciones cuyo core de sistemas está implementado por algún  tipo de enlatado con cierto grado de customización (ERP, CRM, Core bancario, etc). Alrededor de este core la organización desarrolla aplicaciones satelitales que suelen actuar como canales implementado alguna funcionalidad complementaria al core. Estas aplicaciones satelitales/canales suelen tener una gran cantidad de requerimientos y cambios ya que son uno de los medios principales en la actualidad para permitir la innovación del negocio. A la hora de desarrollar estas aplicaciones aparece la necesidad de poder testearlas y que dicha prueba sea (al menos en parte) automatizada.

En particular he tenido la oportunidad de trabajar en contextos de instituciones bancarias y también del ámbito de las telecomunicaciones y en ambos casos he detectado situaciones/desafíos comunes que me parece son habituales en entornos corporativos.

En mi experiencia he identificado dos tipos de desafíos: los técnicos y los humanos.

En lo que respecta a desafíos técnicos el principal pasa por la dificultad de contar con un ambiente de pruebas sobre el cual uno pueda tener control de estados/datos.  En el escenario ideal yo quisiera poder contar con una instancia exclusiva y on-demand del core, cuyo estado pueda manipular a mi gusto sin impactar en otros equipos. Esto no es simple, ya que muchas veces el setup de estos sistemas core es bastante complejo y poco automatizado. Lamentablemente en ninguno de los casos que he trabajo he tenido esta posibilidad, lo cual me ha llevado a buscar soluciones alternativas.

Una situación que me he encontrado en todos los casos que participé es la presencia de un de ESB (enterprise service bus) que funciona como orquestador/integrador de mensajes entre los diversos sistemas de la organización incluido el core. De hecho es común que la interacción de los canales/satélites con el core se haga por medio de un ESB.  Si bien esto puede interpretarse como una complejización de la arquitectura, se supone que en general la simplifica. Al mismo tiempo representa una posible salida al problema del testing.

Escenario corporativo

Entonces ante la imposibilidad de contar con un core para las pruebas (y también para el desarrollo) tenemos dos opciones. Una primera opción es mockear «a la salida» las interacciones con el core.

Opción 1. Mock «a la salida»

La otra opción sería hacer que el ESB devuelva respuestas mockeadas.

Opción 2. Mock en el ESB

Algunos puntos a tener presente independientemente de la estrategia utilizada:

  • Al utilizar una estrategia de mocking en este contexto es necesario tener algún mecanismo para detectar potenciales cambiamos en el sistema que estamos mockeando/emulando. Una estrategia posible es hace contract testing del sistema que estamos mockeando de manera que los tests de contrato se ejecuten periódicamente y nos alerten de eventuales cambios.
  • En algún punto, previo al pasaje al ambiente productivo es necesario poder correr algún tests contra una instancia del core real.  En uno de los casos que me toco participar, la instancia de test del sistema core era tan inestable y distinta al core productivo que lo que se decidió hacer un despliegue canary a un servidor productivo, accesible solo internamente, y de esa forma probar directamente en producción.  Una vez realizada la validación, entonces se completaba el despliegue al resto de los servidores habilitando la aplicación al público en general.

Continuará…..
(en un futuro artículo me referiré a los «desafíos humanos de esta problemática»)

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á…