Situaciones de Diseño #1

Hace tiempo venia con la idea de publicar una serie de videos sobre diversas técnicas/patrones/recomendaciones para lidiar con situaciones de diseño habituales. Finalmente ayer decidí poner manos a la obra y publiqué el primer video de esta serie.

La situación que aborda este primer video es cómo lidiar con lógica que depende de fechas de una forma testeable. Muchas veces cuando tenemos lógica que depende de la fecha actual, estamos tentados de utilizar directamente una llamada al componente nativo de fecha. El problema con este camino es que no resulta testeable. La clave está en encapsular el acceso a la fecha del sistema de manera tal de poner utilizar un doble de prueba (mock) en el momento de hacer nuestra pruebas automatizadas.

Les dejo aquí el video y los invito a que me dejen en los comentarios alguna otra situación que les gustaría que aborde en próximos videos.

Conferencia Testing UY 2021

Este año participé por primera vez como orador en esta conferencia. Más allá de mi experiencia como orador me gustó la organización de la conferencia debido a varias cuestiones que me resultaron innovadoras sobre todo considerando que es un conferencia gratuita organizada por voluntarios.

Además de las tradicionales charlas como la que di yo (de una 1 hora de duración), había también una oferta de talleres (en general de carácter muy práctico y con cupo limitado) y charlas relámpago.

La conferencia se desarrolló durante toda la semana. Los talleres se dictaban durante la mañana/mediodia y primera hora de la tarde. Las charlas tenían lugar por las tardes en horario “after office”.

Más allá de las actividades de contenido, había sorteos y un desafió de testing en equipos. La transmisión de las charlas se hizo por Zoom y todas ellas (las charlas, no los talleres) serán publicadas en el canal de YouTube de la organización.

Como ya es costumbre en tiempos de pandemia, había también un discord a modo de “espacio social”.

Un detalle que me resultó muy interesante es que a pesar de ser un evento “latino” hubó varias presentaciones de otros lugares del mundo, varias de ellas en inglés.

Tuve la oportunidad de participar de varias charlas entre las que se destacaron a mi gusto: la de Guillermo Skrilec sobre el testing de la aplicación de vacunación en Uruguay, la de José Luis Velázquez Jacobo sobre testing de sistemas de conducción asistida y la de Federico Toledo y Matias Fornara sobre calidad de pruebas automatizadas.

Las diapositivas de mi charla está disponibles aquí.

Mis felicitaciones y agradecimiento a los organizadores, creo que ha sido un gran evento, de los mejores de la región.

Curso gratuito de Unit Testing

Una de las dificultades que suelo encontrar al intentar enseñar Test-Driven Development a gente que ya tiene varios años de experiencia en desarrollo de software (con los principiantes es distinto), es que muchas veces no saben hacer pruebas unitarias automatizadas. Por eso, hace un par de semanas cuando decidí experimentar haciendo un curso 100% en video, no dudé en hacerlo sobre unit testing.

Ayer finalmente, luego de un par de semana de trabajo completé el curso, son 2 horas de video divididas en varios videos de no más de 10 minutos. Además de los videos, incluye varios recursos de lectura y ejecicios. Todo el código del curso está basado en C# (netCore) y NUnit, pero todo el contenido es fácilmente extrapolable a otros lenguajes/frameworks. Personalmente creo que tomar el curso completo (videos + lecturas + ejercicios) podría llevar unas 4 o 5 horas dependiendo de la experiencia de programación de la persona.

El curso está publicado con acceso gratuito en la plataforma Udemy

Definición de la estrategia de pruebas

Existen muchas clasificaciones de tests, casi tantas como autores. De caja blanca, de caja negra, unitarias, de integración, de aceptación, funcionales, de sistema, de carga, etc, etc.

Hay para todos los gustos y colores. Más aún, algunos tipos de tests tienen un significado distintos para distintas personas. Un caso usual de esto son los test end-2-end que típicamente:

  • para los desarrolladores implican tests de una funcionalidad que atraviesan todas las capas de la aplicación, desde la pantalla hasta la base de datos
  • para los testers implican tests que cubren un flujo de negocio que integra varias funcionalidades

Esta situación obliga a que todo equipo deba en primer lugar ponerse de acuerdo en la terminología a utilizar. A partir de esto hay que definir la estrategia de pruebas.

Definir la estrategia de pruebas implica definir:

> Tipos de tests que se realizarán
> Cuantos de esos tests se realizarán
> Quien los realizará
> Cuando los realizará
> Qué herramienta se utilizará
> Y cual será la arquitectura de prueba que dará soporte a todos los tests

La siguiente figura resumen parcialmente la estrategia de pruebas de mi proyecto actual.

Los que indica esta imagen se complementa con las siguientes decisiones:

  • El desarrollo se hace una forma test first, donde los tests guían el desarrollo. Todo empieza “tironeado” por una pruebas de aceptación (story tests) especificada en Gherkin a partir de una charla entre: usuario + tester + developer.
  • Los tests de aceptación no se automatizan al comenzar el desarrollo sino a posterior y al mismo tiempo no todos los escenarios son automatizados.
  • El resto de los tests se escriben a priori del código y de a uno por vez. Una vez implementados se ejecutan constantemente en el build server.
  • Salvo los tests de aceptación, todo el resto de los tests de la figura son escritos por los developers.
  • Para los tests de aceptación usamos Gherkin+Specflow.
  • Para los tests de frontend utilizamos Jasmine+Karma.
  • Para los tests de backend utilizamos NUnit.
  • Para mockear utilizamos Moq y Wiremock.

Adicionalmente a los tests de la figura hacemos pruebas de performance (jmeter) cuando tenemos cambios relevantes en la arquitectura y pruebas exploratorias de ejecución manual antes de cada salida a producción. Tenemos pendiente agregar algunas pruebas de seguridad.

Finalmente respecto de cuántos tests de cada tipo hacer idealmente apuntamos a tener un triángulo con pocos tests de aceptación end-2-end para los flujos centrales en la punta y muchos tests unitarios en la base para lograr cobertura de flujos alternativos. En el medio de la pirámide quedan los tests de componentes/integración.

Automatizando pruebas de IBM BPM

Una vez más un amigo que me tiene mucha confianza me invitó a proyecto desafiante: automatizar pruebas de una aplicación generada con una tecnología propietaria “IBM Business Automation Workflow” (IBM BPM).

Muy a grandes rasgos esta herramienta permite automatizar procesos de negocio utilizando una herramienta case que permite integrar (entre otras cosas) servicios (soap & rest) y componentes Java. Una vez que se ha generado el flujo, se genera un paquete (.ear) que se despliega en un servidor de aplicaciones Webphere y listo, está disponible para los usuarios. Hay que mencionar que la propia herramienta de desarrollo de IBM provee ciertas funcionalidades para realizar pruebas, pero en el contexto de esta iniciativa hemos decido ir por un enfoque basado en herramientas de uso general, aunque no descartamos hacer algunos experimentos con estas funcionalidades de prueba.

Luego de un par de sesiones de trabajo hemos identificado 3 tipos de pruebas:

  1. Pruebas end-to-end usando Selenium Web-Driver, esto eso: las prueba es un script que utilizando un nagevador emula el comportamiento del usuario
  2. Pruebas de servicio, realizadas con SoapUI y que consumen los servios SOAP que están por detrás de las pantallas
  3. Pruebas unitarias de los componentes Java codeadas con JUnit.

Decidimos empezar por (2) porque parecía ser un win rápido y efectivamente lo fue. No encontramos mayores complicaciones en hacer estas pruebas.

A continuación empezamos a trabajar en (3) y como sospechamos la cuestión se tornó más difícil porque el código de los componentes Java que se invoca desde la herramienta case (un eclipse customizado) tiene dependencias a bibliotecas de IBM que no están disponibles en repositorios públicos. Para hacer estas pruebas también debimos “mavenizar” los proyectos de cara a poder correr las pruebas en el servidor de itegración continua.

Finalmente, nos queda pendiente probar la estrategia (1) personalmente no me inquieta mucho pues al fin y al cabo las pruebas de UI son algo en lo que no suelo confiar mucho por su costo de mantenimiento y su fragilidad. En dos semanas les cuento que tal nos fue con esto último.

Nuevo proyecto: Arquitectura de Prueba

Una vez más me meto es cuestiones de automatización de pruebas y una vez más en un contexto bancario. Esta vez junto a mis estimados colegas de Grupo Esfera. Ayer hicimos el kickoff del proyecto y a continuación tuve una de las mejores sesiones de trabajo del año junto a mi amigo Diego Fontdevila. Estuvimos unas 2 horas revisando código, hablando de diseño, trade-offs y herramientas 😉

En términos genéricos el objetivo del proyecto es armar una arquitectura de prueba para facilitar/guiar a los equipos de desarrollo de la organización que trabajan en aplicaciones de canales y que deben integrarse con los sistemas core.

El stack de tecnologías con el que voy a estar trabajando incluye: Cucumber, Selenium, Pact, Java y AS400 entre otros.

Lecciones aprendidas automatizando pruebas en ambientes corporativos (parte 2)

En el artículo anterior me centré en los desafíos técnicos. Ahora quiero centrarme en los desafíos humanos, los cuales en ocasiones son aún más complejos: las máquinas hacen lo que uno les dice, pero no las personas no, ¡ja!

El primero de los desafíos humanos aparece en la concepción misma de la iniciativa de automatización. En la mayoría de los casos que he participado la motivación de automatizar ha surgido de una gerente/jefe/líder, pero no de la gente que realiza el desarrollo de los sistemas (developers), ni tampoco de la gente que realiza las pruebas manualmente (testers).

Por lado las iniciativas en las que he participado han caído en manos de equipos incompletos y/o grupos con muy poco compromiso. Gente a la que “se le endosa” la obligación de colaborar en esta iniciativa pero para la cual esta iniciativa no reviste de mayor interés.

Luego de haber participado en varias iniciativas de este estilo solo puedo reconocer 2 de ellas como exitosas (aún cuando los clientes han quedado conformes con lo realizado en casi todos los casos). Los puntos que identifico comunes en esos mencionados casos de éxito son:

  • En ambos casos se contó con una persona en el rol de Software Engineer in Test, alguien que se encarga de proveer/desarrollar herramientas, guías y ayuda en general para facilitar el trabajo de todos aquellos que deban realizar tests.
  • La dedicación pro-activa, comprometida y explícita (en términos de asignación y planificación) de un desarrollador/implementador del sistema core para que asista en el setup de los set de datos necesarios en el sistema core.

En breve estaré empezando un nuevo proyecto de este estilo, por ello en un próximo artículo contaré lo que lo tenemos planeado.

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”)

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