¿Para qué monitorizar la calidad? Aspectos, scores, KPIs y otros duendes

Introducción

Después de tanto tiempo sin escribir en el blog, vuelvo a aparecer para cumplir mi última promesa antes de año nuevo! Espero que estés genial dentro del año que hemos pasado, sino es así te mando mucha fuerza.

Si lees la palabra testing o calidad, es probable que lo primero que te haya venido a la cabeza es la palabra “test” o prueba, como si el desarrollar los teses fuera el objetivo principal de una estrategia de calidad. Esto es normal, y la razón es porque estamos muy acostumbrados a pensar que con tener una buena cantidad de pruebas vamos a mejorar la “calidad”.

Pero, ¿qué es la calidad?, ¿No tener bugs?. ¿Es el bug el indicador principal de la calidad de nuestras aplicaciones?

En este post me gustaría hablar sobre los aspectos que creo que definen la calidad de nuestras aplicaciones, cómo monitorizar la estrategia de testing y cómo ayudarnos en la toma de decisiones.

Aspectos de la calidad

Como he comentado en la introducción, creo que la calidad no debería basarse únicamente en si nuestro producto tiene muchos o pocos bugs, estoy de acuerdo en que es un aspecto importante, pero tenemos que tener un prisma más holista.

En mi caso separo los aspectos de calidad en tres grupos: funcionales, técnicos y productivos.

Aspectos funcionales

Son aquellos que evalúan que el producto se comporta como el usuario/cliente o negocio lo espera. Por ejemplo, podríamos evaluar que el comportamiento es el esperado, la UX etc.

En este post no voy a entrar al detalle de los KPIs para evaluar los aspectos funcionales ya que creo que puede ser interesante poner el foco en los siguientes. ¡Perdonadme! 

Aspectos técnicos

Son aquellos que evalúan la calidad técnica del producto. Estaríamos hablando por ejemplo de vulnerabilidades, code smells, código duplicado, complejidad ciclomática y arquitectónica, deuda técnica etc.

Aspectos productivos

Muy poco valorados en el testing, son aquellos que nos ayudan a medir si el producto bloquea o no a la productividad del equipo. Por ejemplo podríamos evaluar cuánto tiempo nos lleva corregir un bug, añadir una nueva funcionalidad, falsos en los tests etc.

Cómo veis, no hemos hablado de bugs como tal en ninguno de los aspectos. Este punto es interesante y abre el debate de lo que consideramos como bug y en qué aspectos recaería este concepto. 

Si consideramos el bug únicamente como algo técnico, es decir, un cambio en el código que provoca posibles fallos o errores, lo podemos ubicar en el aspecto técnico. Pero, ¿Y si ese fallo es debido a una falta de definición o un corner case lo llamamos “bugature”?

Se está creando una tendencia a categorizar los bugs, por ejemplo: bug técnico, bug en producción o en desarrollo, bug de infraestructura, bug de definición etc. Esto nos ayudaría a poner el foco y encontrar el punto de mejora mucho más fácil. Cómo he dicho, esto da para un buen debate.

Tenemos que cambiar nuestra forma de pensar y de entender la estrategia de calidad. Debemos darnos cuenta de que las técnicas de testing las aplicamos no sólo para encontrar o prevenir los bugs, sino para garantizar de alguna manera estos aspectos y la productividad del equipo.

¿Entonces, no desarrollamos las pruebas solo para encontrar o prevenir bugs? No. No dejes que el árbol no te deje ver el bosque, toma frase de abuelo cebolleta.

KPIs y el score de calidad

Vamos a la parte interesante. ¿Qué KPIs seguimos para evaluar todos estos aspectos y qué “demontres” es eso del score de calidad? 

Me gustaría decir que a día de hoy he llegado a evaluar todos los aspectos que he comentado en la introducción con KPIs, pero como os he dicho, la realidad es que no es así. Solo he tenido oportunidad o solo he conseguido evaluar algunos de ellos. 

KPIs de los aspectos técnicos

En la parte técnica es donde suelo tener mayor número de KPIs dado que son más fáciles de obtener y evaluar los datos.

  • Número de bugs vivos. Los bugs vivos los identifico como aquellos que no están en un estado resuelto. Cuando este KPI crece es realmente interesante ver si es porque el producto se degrada, o porque tenemos que mejorar la gestión del panel de bugs dentro del equipo.
  • Número de bugs vivos VS tareas vivas. Es un KPI interesante para ver dentro de todas las tareas que tenemos que parte de la tarta se las lleva los bugs. A día de hoy he dejado de usar este KPI ya que realmente no lo tenía en cuenta para la toma de decisiones.
  • Número de bugs creados por prioridad. Realmente no nos interesa hablar de cantidad, sino de prioridad o de impacto.
  • Número de bugs resueltos por prioridad. Al igual que el KPI anterior, nos interesa saber el número por prioridad o impacto, ya que realmente es lo que nos va a hacer decidirnos que bug solucionamos primero.
  • Estado del análisis estático. Este dato es un agregado de otros KPIs. Por ejemplo con una herramienta como Sonar podemos evaluar el estado del código: vulnerabilidades, code smells, seguridad, mantenibilidad, código duplicado, complejidad, cobertura etc.

En este punto me gustaría recomendaros utilizar pruebas de mutación, ya que el análisis estático nos va a evaluar el resultado de su ejecución, viendo así la calidad de nuestras pruebas. Este dato si lo analizamos junto con la cobertura, realmente tendremos un resultado bastante más objetivo que lo que es realmente analizar  la propia cobertura.  

KPIs de los aspectos productivos

Las estrategias de testing no solo tienen que ayudarnos a prevenir el número de bugs, también deben ayudar al equipo a ser más productivos. Tenemos que conseguir una estrategia que equilibre la calidad y productividad. 

  • Tiempo de resolución de bugs por prioridad. Importante para ver si estamos resolviendo los bugs en los tiempos estimados por prioridad o simplemente estamos priorizando bien.
  • Número de bugs descartados. ¿Estamos creando bugs que no lo son?, ¿Reproducimos los bugs antes de darlos de alta?  Este KPI nos va a dar el estado de la calidad de nuestro panel de bugs.
  • Puntos de historia desarrollados. Puede ser interesante si queremos saber si los cambios en nuestra estrategia de testing están afectando al delivery. Este es uno de los KPIs con los que más dudas he tenido en implementar ya que no es del todo determinista, pero sí puede servir como alerta.
  • Números de tareas no validadas. Este KPI es uno de los más importantes de cara a valorar si la estrategia preventiva funciona. Vamos a ver cuantas tareas se echan atrás en el proceso de code review por un bug en el código, y por qué no lo han detectado las técnicas preventivas.
  • Falsos positivos/negativos en las pruebas. Es un KPI que puede ayudarnos a valorar tanto la calidad de nuestras pruebas como el impacto en la productividad que tienen. Realmente no lo he llegado a implementar ya que el decidir si los resultados de las pruebas son falsos o no, puede ser un proceso manual. A día de hoy estoy dando vueltas para ver si es interesante implementarlo.

Score de calidad

Ya hemos visto el número de KPIs que podemos utilizar para valorar la calidad de nuestro producto, pero ¿realmente vamos a saber utilizar tanta información y tanto numerito para hacer una evaluación?

Donde trabajo actualmente hemos creado un sistema donde podemos definir un perfil de reglas que puntúan los diferentes KPIs, devolviendonos el score del producto. De esta forma evaluamos objetivamente el software y vemos su evolución. Si observamos que el score del producto se degrada, con entrar a ver la puntuación asignada por cada KPI veremos donde está el problema y podemos tomar acciones.

De forma resumida así es más o menos como funciona nuestro sistema:

  1. Damos de alta el proyecto que queremos analizar en el sistema.
  2. Asignamos un nuevo “minero” al proyecto para que se ejecute cada X horas y extraiga la información de las tareas y los bugs del proyecto.
  3. Cada resultado del análisis estático del proyecto se enviará al sistema.
  4. Cada vez que el proyecto se actualice con nueva información de calidad, el algoritmo re-calculará el score en base a un perfil de reglas que se le ha asignado al proyecto. 

Un ejemplo de perfil de reglas (score profile) podría ser:

En nuestro caso la puntuación de cada proyecto va de 0 a 100, asignándole una label de calidad, A, B, C o D, dependiendo el valor del score.

De momento los scores de calidad nos están dando buen resultado para ver cómo afecta la estrategia de calidad al proyecto. 

Otra recomendación que os doy, es que si trabajáis con OKRs de calidad podéis utilizar el score como OKR ya que agrupa diferentes KPIs, es totalmente medible y es fácil poner objetivos alcanzables.

Analizando los datos para nuestra estrategia

El score como norma

Como hemos dicho anteriormente lo interesante es ver la evolución del score del producto y poder corregir a tiempo su degradación. Si observamos que el score baja podemos ver en detalle la evaluación del algoritmo y tomar las acciones que creamos necesarias para revertirlo.

Analizando los KPIs en detalle

A parte del score, también es interesante tener una visión en detalle de los KPIs y sus tendencias. Esto siempre nos puede ayudar poner el foco y valorar los cambios que tenemos que hacer en la estrategia de testing. 

Ejemplos:

  • El número de bugs aumenta y los code reviews suelen ser positivos. Puede ser que necesitemos invertir esfuerzos en la mejora de las pruebas, puede ser que tengamos que mejorar el proceso de code review etc.
  • El tiempo de resolución de los bugs de prioridad media es muy alto. Puede ser debido a que el equipo no gestiona bien la prioridad, puede ser que no se invierta puntos de historia en el sprint para solucionar bugs etc.

Veamos otro ejemplo, esta vez con un análisis de las tendencias. El siguiente gráfico muestra la evolución de los bugs creados, resueltos y rechazados.

  1. Vemos como en el primer punto el pico de bugs se dispara. Un primer análisis puede indicarnos que no se han invertido esfuerzos en implementar una estrategia de testing.
  2. Como primera acción se decide implementar técnicas reactivas y reducir el número de bugs, por lo que la tendencia baja, pero no tomamos medidas preventivas.
  3. El decidir no invertir esfuerzos en técnicas preventivas hace que el pico se vuelva a elevar. Por lo que se decide esta vez sí invertir esfuerzos en medidas preventivas.
  4. Tras aplicar una estrategia de testing preventiva vemos como el producto se va poco a poco estabilizando.

Todos estos datos nos ayudan a entender cómo afecta nuestra estrategia de testing al producto, es una forma de evaluarla. Pero no nos engañemos, tenemos que hacer un análisis más profundo del asunto, ya que también saca a relucir otro tipo de puntos de mejora, por ejemplo en la gestión del equipo o del producto.

  • ¿El no tener una estrategia de testing ha podido ser por falta de responsabilidad en el equipo, por prisas por sacar un producto o una funcionalidad?
  • ¿No tenemos una buena cultura de calidad?
  • ¿No se han definido bien las historias?

Y un largo etc, es decir, tenemos que hacer un análisis holista y por supuesto no podemos utilizarlos para juzgar, sino para buscar puntos de mejora. 

Conclusiones

Si habéis llegado hasta aquí puede ser que os haya interesado al menos un poquito el post, cosa me alegra!

Como conclusión me gustaría recalcar que tenemos que tomar los KPIs o el score como un sistema de control y alerta que nos indique si tenemos que hacer cambios en cómo aplicamos las técnicas de testing.

Tenemos que ser abiertos y entender que cada producto y equipo es diferente y lo que funciona en uno, no tiene porque funcionar en otro. Ninguna estrategia es una panacea y lo normal es que sea viva, se adapte y evolucione. ¿Podríamos llamarlo relatividad estratégica? ¡Me gusta la idea!

Tenemos que empezar a pensar que el testing no solo es para reducir el número de bugs, sino también lo es para ayudar a mejorar y optimizar la productividad y seguridad del equipo.

En mi caso la pelea con los KPIs es continua, es decir, lo que hoy me funciona, mañana igual no, es por eso que os invito a probar a implementar nuevos y a no tener miedo de descartar aquellos que creais que no os valen. 

Otro consejo que a mí me ha ayudado mucho a mejorar la cultura de la calidad en los equipos y promover la responsabilidad compartida es visibilizar y hacer partícipe al equipo de este dashboard. Si el equipo lo tiene presente y ven que van mejorando los datos o que un “para qué voy a meter un test unitario” hace que el score se degrade, les va a sensibilizar y a concienciar.

Creo que estos datos enfocados de otra manera, pueden ser muy interesantes para mostrar a negocio, también para que entiendan el esfuerzo que se hacen en el equipo y la importancia de que las prisas no son buenas o que la calidad no es algo que se pueda sacrificar.

¡Y esto es todo!

¡Feliz Solsticio de Invierno, Navidad, Hanukkah o lo que vuestras costumbres dicten!

Dimensiones entre pruebas aisladas e integradas. Flakiness de las pruebas

INTRODUCCIÓN

Seguimos con la serie de “Dimensiones entre pruebas aisladas e integradas”. En los post de la serie anteriores hemos hablado sobre el grado de confianza de las pruebas y del tiempo de test de las pruebas. Vimos como el tener un mayor área de impacto ayudaba a tener un mayor grado de confianza y como el tiempo de test de las técnicas de testing es un valor que puede declinar la balanza en las decisiones de nuestra estrategia.

Existe otro punto importante en el que debemos basar las decisiones de nuestra estrategia de testing. En este post hablaremos sobre el test flakiness de cada tipo de pruebas, y al igual que en los post anteriores pondré el gráfico de dimensiones comparativo de cada tipo de pruebas.

TEST FLAKINESS

Cuando hablamos de “flaky tests” hablamos de debilidad, es decir, de la capacidad de que las pruebas fallen con facilidad por razones externas a los cambios realizados. Los flaky tests son uno de los quebraderos de cabeza más grandes del mundo del testing, sobre todo del testing funcional.

La debilidad de las pruebas pueden venir por diferentes motivos, los llamaremos flaky points o puntos de debilidad: conexión, estrategía de datos de prueba, infraestructura etc.

Puntos de debilidad

¿Cuántas veces hemos comenzado a desarrollar pruebas sin poner atención en la arquitectura del proyecto o en la arquitectura de test que tenemos planteada? En mi caso, me ocurría mucho. 

La arquitectura de test que tengamos planteada en nuestra aplicación es de suma importancia a la hora de desarrollar pruebas, dado que nos marcará no solo las necesidades de testing que tenga el proyecto, si no algo igual de importante, los puntos de debilidad.

Los puntos de debilidad o flaky points son aquellas partes o áreas del código, arquitectura y de las pruebas que pueden ser propensas a fallar debido a razones externas a nuestro código o nuestros cambios.

Los puntos de debilidad pueden ser muy variados, dependiendo de las características de nuestros proyectos, pero principalmente solemos encontrarnos con:

  • Datos: Uno de los problemas más comunes si no tenemos una buena estrategia de datos es que las pruebas fallen debido al conflicto de los datos y las dependencias entre las diferentes pruebas. Esto suele ser debido a reutilizar datos, a tener un repositorio compartido etc.
  • Infraestructura: Otro fallo común por ejemplo en arquitectura en microservicios es cuando necesitamos tener todos los servicios que necesitamos desplegados y con la versión correcta. En muchas ocasiones nos encontramos que eso no es así, debido a muchos motivos, entre ellos que tengamos que competir por el mismo entorno de pruebas y exista el conflicto entre versiones. 

    Puede ocurrir también que haya parte de la infraestructura que necesitemos que no esté bien levantada o versiones de sistemas de bases de datos etc. 

    En resumen, nos podemos encontrar con un espectro muy amplio de problemas.
  • Comunicación: Los fallos de red suelen ser un problema en el que nos tengamos que enfrentar en ciertas ocasiones. Otro problema puede ser el tiempo de carga, el tiempo de respuesta, lidiar con asincronía etc.

    Imaginemos una web, pongamos que tardamos casi un segundo en obtener los datos de una consulta y popular la tabla en una vista con estos datos, puede que nuestra prueba falle debido a que intenta comprobar la existencia de los datos antes de que se obtengan. 

En la imagen vemos identificados los diferentes puntos de debilidad dentro de nuestra arquitectura, una imagen en mente nos debería ayudar a definir la estrategia de testing que buscamos. Es importante tenerlos localizados para comenzar a tomar decisiones que ayuden a paliar estos puntos.

Una de las formas de paliar y reducir los puntos de debilidad de las pruebas es aislarlas. Aislando las pruebas reducimos notablemente estos puntos ya que no necesitamos comunicarnos con los colaboradores externos, los datos de prueba deberían estar acotados únicamente a la prueba y la infraestructura se levanta ad-hoc a la prueba.

Una cosa importante a destacar es que el punto de debilidad de datos lo podemos heredar en las pruebas aisladas si no seguimos una buena estrategia de datos aislada. Puede que se minimice el impacto debido a que la base de datos no es compartida entre diferentes proyectos o equipos, pero sí que podemos llegar a tener dependencia de datos entre nuestras pruebas, manteniendo así el punto de debilidad de datos.

Comparando el flakiness entre los tipos de prueba

Bueno, ya tenemos claro lo que es el flakyness y lo que son los puntos debilidad, ahora toca medirlo junto a otras dos dimensiones:

  • Flakiness, representado por círculos con más o menos radio, en base al grado de debilidad de la prueba.
  • Ámbito (scope) de las pruebas, en el eje X. Se trata de comprobar una parte del código, una funcionalidad concreta o un E2E.
  • Etapa (stage) de las pruebas, en el eje Y. Fase en la que se lanzan las pruebas en local durante el desarrollo, en el CI, o en fases de preproducción o producción.

CONCLUSIONES

Durante toda la serie de post, hemos visto diferentes dimensiones que nos pueden ayudar a tomar la decisión de cuándo implementar o si llegar a implementar una técnica de prueba en nuestra estrategia.

Diseñar una estrategia de testing no es fácil. No se resume en tener una suite de pruebas unitarias, integración, funcionales etc. gigantesca, dado que esto nos puede llegar a perjudicar la eficiencia y la productividad del equipo. Tenemos que llegar a balancear todos los aspectos y llegar a un balance entre productividad y calidad.

Este balance lo podemos ganar a través de implementar una estrategia de testing basado en la prevención, con más técnicas aisladas para obtener un feedback temprano del estado de la calidad de nuestra aplicación. Necesitamos también tener técnicas integradas como pruebas de soporte en etapas más reactivas, de forma que nos ayuden a obtener una mejor visión de la calidad del software pero sin llegar a parar o a bloquear las releases.

Espero que la serie os haya parecido de interés y que os ayude a la hora de definir las estrategias de testing, o a entender que diseñarla no es tarea fácil.

POSTS DE LA SERIE

1/3 Dimensiones entre pruebas aisladas e integradas. Grado de confianza.

2/3 Dimensiones entre pruebas aisladas e integradas. Tiempo de test.

3/3 Dimensiones entre pruebas aisladas e integradas. Test flakiness.

Dimensiones entre pruebas aisladas e integradas. Tiempo de test.

INTRODUCCIÓN

Seguimos con la serie de “Dimensiones entre pruebas aisladas e integradas”!. En el post anterior hablábamos sobre el grado de confianza de las pruebas y cómo el tener un mayor área de impacto ayudaba a tener un mayor grado de confianza.

Para diseñar o definir nuestra estrategia de pruebas no nos podemos basar solo en el grado de confianza, es necesario tener en cuenta otras dimensiones. En este post hablaremos sobre el tiempo de test e iteración de cada tipo de pruebas, al igual que en el post anterior pondré el gráfico de dimensiones comparativo de cada tipo de pruebas.

TIEMPO DE TEST E ITERACIÓN DE LAS PRUEBAS

Antes de ver el gráfico de dimensiones, lo primero que tenemos que entender es cómo medimos el tiempo de test.

Tiempo de test

El tiempo de test es el tiempo que tarda la prueba desde que se lanza hasta que obtenemos los resultados, sencillo, ¿no?. Normalmente lo podemos medir por:

Test time = Build time + Deployment time + Test setup time + Execution time + Test clean time +  Report time.

Resumido en:

TT = BT + DT + ST + ET + CT + RT

No todos los tipos de prueba suman estos tiempos, por ejemplo las pruebas unitarias no tienen tiempo de despliegue, por lo que, DT = 0. 

Otro ejemplo son las pruebas funcionales aisladas, que al tiempo de test hay que añadir el tiempo que tardamos en levantar (EUT – Environment up time) y tumbar (EDT – Environment down time) el entorno aislado. Podríamos poner las siguientes formulas por tipo de prueba:

  • Unit test: TT = BT + ST + ET + CT + RT
  • Contract test: TT = BT + ST + ET + CT + RT.

    El ST ya contempla el tiempo que tarda en descargarse la nueva versión del contrato en caso de que haya.
  • Integrated integration test: TT = BT + ST + ET + CT + RT
  • Isolated integration test: TT = BT + EUT + ST + ET + CT + EDT + RT
  • Integrated functional test: TT = BT + DT + ST + ET + CT + RT
  • Isolated functional test: TT = BT + EUT + ST + ET + CT + EDT + RT

    En las pruebas aisladas lo normal es no desplegar la aplicación, si no levantarla como parte del entorno, por eso el tiempo de despliegue se incluye en EUT.
  • Support functional test: TT = BT + DT + ST + ET + CT + RT
  • Exploratory test: TT = BT + DT + ST + ET + CT + RT
  • Production functional test: TT = ST + ET + CT + RT

En sí estas fórmulas no nos dicen mucho, pero nos pueden servir para conocer donde se van los tiempos de nuestras pruebas. Algo curioso es que por el hecho de tener más variables en la fórmula no significa que el tipo de prueba sea más lento que otra que tenga menos variables.

Los tiempos de las pruebas aisladas contemplan EUT y EDT, que no están contempladas en las pruebas integradas, pero estas últimas son mucho más lentas. Esto es debido a que en las integradas en el tiempo de ejecución (ET) hay que contar que se pueden comunicar con una BD real y con otros colaboradores como servicios etc, llevándose la mayor parte del tiempo.

En la imagen vemos como las pruebas aisladas al conectarse en local contra un snapshot de BD y contra los stubs de los servicios, el tiempo de comunicación se reduce bastante.

La BD únicamente tendrá los datos necesarios de la prueba, y el stub del servicio simplemente nos devolverá la respuesta que esperamos, es decir, no tenemos que esperar a que se ejecute nada en los servicios externos.

Comparando el tiempo de test entre tipos de prueba

Ahora que ya entendemos en qué nos basamos para valorar el tiempo de test vamos a medirlo junto a otras dos dimensiones:

  • Tiempo de test, representado por círculos con más o menos radio, en base al tiempo total.
  • Ámbito (scope) de las pruebas, en el eje X. Se trata de comprobar una parte del código, una funcionalidad concreta o un E2E.
  • Iteraciones (iteration) de las pruebas, en el eje Y. Cada cuanto ejecutamos las pruebas, cada cambio, antes de cada commit, en el CI, en el entorno de testing, pre-producción o planificadas o disparadas por algún evento en concreto.

CONCLUSIONES

Lo más interesante de ver a este nivel, es lo unido que está el tiempo de test con el número de iteraciones. 

Para que el testing ayude a mejorar la productividad del equipo, la idea es tratar de obtener de forma rápida el estado de la aplicación tras un cambio. Por este motivo las técnicas más rápidas se intentan ejecutar en las iteraciones más tempranas del desarrollo.

En este punto es donde las técnicas de testing aisladas nos van a aportan un mayor valor, ya que nos van a permitir ejecutar en iteraciones tempranas y de forma rápida aquellas pruebas que se deberían ejecutar en etapas posteriores, con un mayor coste temporal y por lo tanto, productivo. 

Si nos basamos en una estrategia de testing preventiva, lo que nos va a interesar es ejecutar la mayor parte de las técnicas de prueba en etapas tempranas. Vamos a intentar ejecutar las pruebas por cada cambio que hagamos o antes de cada commit o merge, lo que nos va a hacer apostar por técnicas con un tiempo de test rápido y con un grado de confianza medio alto.

POSTS DE LA SERIE

1/3 Dimensiones entre pruebas aisladas e integradas. Grado de confianza.

2/3 Dimensiones entre pruebas aisladas e integradas. Tiempo de test.

3/3 Dimensiones entre pruebas aisladas e integradas. Test flakiness.

Dimensiones entre pruebas aisladas e integradas. El grado de confianza.

INTRODUCCIÓN

Tras la charla “Isolated vs Integrated tests” que presenté en el Open Space de la WeCode 2020 pedí feedback a varios compañerxs y amigxs de la comunidad. Entre ellos a Eduardo Ferro, persona muy respetada por su experiencia y con el que he coincidido en varios eventos, además de haber podido participar en un debate en NorthemQuality.

Podéis ver el debate (QA y principios XP, ¿Juntos o Revueltos?) en los vídeos de la primera temporada.

Mi idea principal era presentar esta charla en la T3chFest, pero la descartaron. Menos mal, no me hubiera dado tiempo, además de poder llegar a ser muy densa. Lo primero que me dijo Edu cuando le pedí feedback fue: “No te daba tiempo ni de palo” (aunque creo que lo de “ni de palo” lo he añadido yo a mis recuerdos). 

Edu me comentó que la idea era interesante y que se había apuntado algunas cosas, pero que podía llegar a costar ver las diferencias entre las pruebas integradas y aisladas, en definitiva, que podría llegar a ser un poco lío. 

Me recomendó hacer varios gráficos que representarán la idea que quería exponer, gráficos con varias dimensiones para ver las diferencias entre cada tipo de prueba.

Me puse a darle vueltas, se me ocurrieron varios gráficos con distintas dimensiones y de aquí nació esta serie de posts..

Os dejo un enlace de este mismo blog para aquellas personas que no conozcan de qué tratan las pruebas integradas y aisladas.

En esta serie de posts vamos a hacer la comparativa entre varios tipos de prueba. En el testing existen un número elevado de técnicas, para el artículo he elegido las siguientes:

  • Pruebas unitarias.
  • Pruebas de contrato.
  • Pruebas de integración integradas.
  • Pruebas aisladas de integración: A diferencia de la pruebas de integración, en las aisladas en vez de conectar con los colaboradores reales, vamos a conectar con componentes (stubs) para simular su conexión.
  • Pruebas funcionales integradas.
  • Pruebas funcionales aisladas: Al igual que en las pruebas aisladas de integración, haremos uso de stubs para simular la funcionalidad de los colaboradores y así no depender de ellos.
  • Pruebas funcionales de soporte: Nos van a servir para detectar aquellos bugs que no deberían bloquear la release. Suelen emplearse como complemento a las funcionales integradas y aisladas.
  • Pruebas exploratorias.
  • Pruebas funcionales en producción: Pruebas que intentan verificar una funcionalidad en producción, normalmente para comprobar el estado de la aplicación.

Vamos a ello!

GRADO DE CONFIANZA DE LAS PRUEBAS

La dimensión que vamos a tratar en este post es la del grado de confianza de las pruebas. 

Cuando hablamos de grado de confianza, estamos hablando de la capacidad de detección de bugs de una prueba. Lo importante es conocer el área de impacto de la prueba en nuestra aplicación, cuanto más impacte, más grado de confianza podrá darnos. Vamos a diferenciar dos tipos de área de impacto:

  • Área de impacto principal (MIA): Es el área objetivo de la prueba.
  • Área de impacto secundario o área de impacto irradiada (IIA): Es el área que se ve afectada sin ser objetivo principal de la prueba.

Lo vamos a entender mejor en el siguiente análisis por cada tipo de pruebas. En las siguientes imágenes tenemos representada una aplicación con arquitectura hexagonal, veamos las áreas de impacto de los diferentes tipos de pruebas aisladas e integradas.

Área de impacto en pruebas de integración integradas y aisladas

En las pruebas de integración integradas, vemos cómo el área de impacto principal (MIA) afecta a la BD y a la clase que implementa la integración con la BD (repository adapter).

Tiene sentido, puesto que es lo que queremos comprobar en este tipo de pruebas, pero la capa de dominio se ve afectada en nuestra aplicación debido a que en nuestro ejemplo accedemos a las clases de dominio en el repository adapter. Este área es identificada como área de impacto irradiada (IIA).

En las pruebas de integración aisladas, el área de impacto principal afecta al repository adapter. En este caso, el snapshot de la BD pasa a ser un área secundaria ya que no es la BD “real”, por lo que tampoco probamos su configuración. 

Después de ver las áreas de impacto de los dos tipos de pruebas, podemos valorar que el grado de confianza de las pruebas integradas de integración es mayor que el grado de confianza de su homónimo aislado.

Área de impacto en pruebas funcionales integradas y aisladas

En el caso de las pruebas funcionales tanto integradas como aisladas, es más complejo ver o diferenciar entre las áreas de impacto principales e irradiadas, esto es debido a que al comprobar una funcionalidad el MIA abarca casi todo este scope.

En las siguientes imágenes vamos a ver cúal es el área de impacto de una prueba que ataca una funcionalidad concreta, en el caso de una prueba funcional integrada y la misma de forma aislada.

El grado de confianza seguirá siendo mayor en la pruebas integradas, no obstante aislarlas nos dará ciertos beneficios como poder lanzarlas en un entorno local de desarrollo. 

En el caso de la pruebas funcionales de soporte y exploratorias el área de impacto sería el mismo que las que hemos visto en las integradas ya que seguimos con una comunicación real con nuestra BD, servicios etc.


Área de impacto en pruebas de contrato

En el caso de las pruebas de contrato tenemos que valorar el área de impacto dependiendo de si son pruebas de proveedor o consumidor. 

En el caso de las pruebas de proveedor el MIA se focalizará en los controladores, al mockear sus colaboradores no habrá áreas irradiadas. En el caso de las pruebas de consumidor el MIA afecta a la clase cliente (service adapter), pero en este caso la capa de dominio si se ve afectada por el IIA, ya que la clase cliente implementa clases de dominio. 

Área de impacto en pruebas unitarias

En las pruebas unitarias el área de impacto principalmente se focaliza en la clase testeada en el caso de ser unit solitary tests. Por otro lado, en el caso de ser sociable unit test tendemos áreas de impacto irradiadas.

Podéis seguir este enlace para conocer los dos tipos de pruebas unitarias.

Comparando el grado de confianza entre tipos de prueba

Ahora que hemos visto lo que es el grado de confianza por cada tipo de prueba, vamos a ver el gráfico de dimensiones que comentábamos al principio del post. Mediremos:

  • Grado de confianza, representado por círculos con más o menos radio, en base al grado de confianza.

    Es importante destacar que el grado de confianza lo he valorado por la capacidad de impacto del tipo de prueba, por ejemplo, las pruebas unitarias pueden tener impacto en la mayor parte del código.

    Al igual que en el caso de las pruebas aisladas funcionales frente a las funcionales o E2E de soporte, que tendrá un mayor grado de confianza debido a que lo normal es tener un mayor número de pruebas aisladas que pruebas integradas de soporte.
  • Ámbito (scope) de las pruebas, en el eje X. Se trata de comprobar una parte del código, una funcionalidad concreta o un E2E.
  • Etapa (stage) de las pruebas, en el eje Y. Fase en la que se lanzan las pruebas en local durante el desarrollo, en el CI, o en fases de preproducción o producción.

CONCLUSIONES

El grado de confianza no debería darnos a elegir entre un tipo de prueba u otra, como vemos en el gráfico, cada prueba tiene un scope y un stage diferente, es por eso que son complementarias unas con otras. Por el contrario, sí nos puede ayudar en definir nuestra estrategia de testing.

Las pruebas aisladas deberían estar más presentes en las fases tempranas del desarrollo, de esta forma vamos a poder desarrollar de forma más fiable y destapar cuanto antes los bugs más críticos o bloqueantes. 

Las pruebas integradas deberían ayudarnos a destapar aquellos errores que vengan por infraestructura o aquellos que no sean bloqueantes. Deberían ser pruebas de soporte que nos ayuden a obtener mayor información sobre el estado de nuestra aplicación.

No obstante cómo he dicho, la estrategia de testing que sigamos debe estar alineada con el contexto situacional. La estrategia puede y debe ir pivotando y creciendo de forma progresiva, adaptándose poco a poco.

POSTS DE LA SERIE

1/3 Dimensiones entre pruebas aisladas e integradas. Grado de confianza.

2/3 Dimensiones entre pruebas aisladas e integradas. Tiempo de test.

3/3 Dimensiones entre pruebas aisladas e integradas. Test flakiness.

P&R – Balancear la productividad del equipo y la calidad del producto

INTRODUCCIÓN

Ya hemos hablado anteriormente en este blog sobre la estrategia Prevention & Reaction (P&R) y cómo aplicarla en el equipo con un rol como el QDev.

Ya comentamos de la importancia de que fuera una estrategia viva y adaptable, es decir, que fuéramos capaces de pivotar sin ver afectada la calidad ni la productividad del equipo.

Pero, ¿Cómo la controlamos o monitorizamos su estado?

Donde trabajo actualmente llevo unos meses intentando crear la cultura y la estrategia de calidad. Al principio hemos tenido que definir unos KPIs muy generales y monitorizar la estrategia de forma manual. 

Como comienzo para ir aplicando e iterando en la estrategia no era una mala opción, pero hemos evolucionado. Se ha conseguido que los equipos sean más autónomos aplicando la estrategia definida, por lo que ya no nos vale monitorizar así. 

Contaré mi experiencia sobre cómo aplico el de rol de Head of QA en otro post 😛

P&R se apoya mucho sobre el concepto de smart testing, por lo que tenemos que hacer una monitorización inteligente y con decisiones automáticas basadas en datos.

En esta serie de posts hablaremos de dos conceptos importantes dentro de la estrategia P&R:

  • Balancear la red de seguridad de calidad y la productividad del equipo
  • KPIs y monitorización de la estrategia

Empezaremos por el primer concepto.

¿Qué tiene que ver la productividad con la calidad?

Bajo mi punto de vista y en nuestra estrategia, mucho. P&R se basa principalmente en la prevención, y las técnicas preventivas operan en la etapa de definición y desarrollo, por lo que tienen impacto directo sobre la productividad del equipo. 

La técnicas de testing que apliquemos no solo van a ayudar a “garantizar la calidad” del producto, si no, que van a ayudar a desarrollar con más seguridad y por tanto de una forma más eficiente.

Si abogamos por una responsabilidad compartida de la calidad en el equipo, no va a existir un rol (Tester, QA, QE, Test Engineer etc.) que sea el único en aplicar las técnicas de testing o en automatizar las pruebas  y gestionar bugs.

Todo el equipo tendrá que aplicarlas y desarrollar las pruebas junto con el QDev, quien se hará cargo de la estrategia de calidad del equipo o del producto.

Muchos conocemos la discusión en la comunidad sobre externalizar el testing fuera de la empresa o incluso tener un equipo de calidad dentro y trabajar en un equipo multidisciplinar, pero de lo que no se habla tanto es de que también se externaliza el testing dentro de un equipo multidisciplinar. 

Veamos un ejemplo:

Tenemos un equipo compuesto por 1 QA vs 4 Devs, y en la que la responsabilidad del testing (salvo los code review y unit/integration testing) es del QA. Entre sus tareas está la de gestionar los bugs, diseñar y automatizar pruebas, validar las issues, hacer testing exploratorio etc.

Cuello de botella en el flujo de trabajo

La velocidad de desarrollo siempre será mayor que la del QA, generando un cuello de botella y como resultado:

  • Mala eficiencia 
  • Pérdida de calidad en el producto.
  • Mayor frustración.

Podemos decir, metemos más personas de QA y por tanto reducimos el cuello de botella, todos sabemos que la matemática no es perfecta en estos casos. 

¿Cómo balanceamos la productividad con el desarrollo de la red de seguridad?

¿Los desarrolladores tienen que hacer testing? Es una de las preguntas que pueden surgir en debate. 

Bajo mi opinión, la respuesta es, sí. Si creemos que el desarrollador tiene que hacerse cargo de la tarea desde que la comienza hasta que está en producción, tendrá que cerciorarse de que sale con calidad. 

Es decir, desarrollar la funcionalidad, diseño y desarrollo de las pruebas necesarias (unitarias, integración, funcionales, performance etc) y validar la issue etc, forma parte de la tarea y es responsabilidad del desarrollador y el code reviewer. 

Esto no quiere decir que el QDev (en este caso) no tenga que hacer testing, claro que tiene que hacerlo. Su responsabilidad recae en crear la red de seguridad, para la que desarrollará herramientas y utilidades (las pruebas son consideradas herramientas) así como liderar la estrategia y la cultura de calidad dentro del equipo.

Lógicamente esto tiene un coste en lo que la estimación de la tarea se refiere, ya no solo es desarrollar la funcionalidad, por lo que una tarea que tuviera un coste subjetivo de talla M, tendría una talla objetiva de L. 

¿Porqué hablo de coste subjetivo? Veamos:

Coste subjetivo en la estimación

Coste subjetivo en la estimación de la tarea

Lo normal es estimar cuánto se tarda en desarrollar, y en el mejor de los casos también se asume el coste del CR y el despliegue a prod. Pero el problema viene en que no asumimos el coste del testing muchas veces al no estar integrado en el trabajo del desarrollador, por lo que el coste estimado de la issue no se acercará a la realidad. 

Como vemos en la imagen, es prácticamente imposible estimar de forma objetiva si no se integra el testing en la fase de desarrollo, ya que se depende de la finalización de una tarea de la que es totalmente dependiente.

Si nos fijamos, esa dependencia causa un cuello de botella cada vez mayor, esto es debido a que el desarrollador necesita que se valide su issue, una vez ya la ha desarrollado, haya pasado el CR y haya desplegado en un entorno de pruebas. 

En caso de un feedback negativo, deberá volver a la fase de desarrollo, CR, deploy a pruebas y volver a repetir el proceso. Un coste productivo bastante alto.

Yo siempre digo que para mí, el “fracaso” no está sólo en encontrar un error o un bug en producción, si no en el entorno de pruebas, debido a que solucionarlo tiene un coste alto, menor que el de producción, claro está. 

Es por eso que creo en la necesidad de invertir en técnicas preventivas.

Coste objetivo en la estimación

Coste objetivo en la estimación de la tarea

Por el contrario, si se ve la tarea como el conjunto del desarrollo de la funcionalidad y el diseño, desarrollo y ejecución de las pruebas, a cargo del desarrollador, mejoraremos la eficiencia y nos acercaremos más al coste objetivo de la tarea.

Lo más importante de seguir esta metodología en cuanto al testing, es que vamos a tener un feedback temprano en fase de desarrollo, y no en fases posteriores.

Las pruebas dejan de ser jueces y pasan a ser la red de seguridad de calidad. Incluso los desarrolladores pueden trabajar con TDD, BDD etc. 

La probabilidad de encontrar un fallo o un bug en fases de pruebas posteriores se verá reducida ya que se habrán detectado y arreglado en fase de desarrollo, siendo el coste productivo mucho más bajo.

El o la QDev intentará equilibrar el coste productivo y la calidad mediante el desarrollo de herramientas de testing y la estrategia de calidad del equipo.

El equipo

Para poder llegar a trabajar con este modelo, es necesario que exista una cultura de calidad dentro del equipo. Los integrantes deberán tener unos conocimientos mínimos de testing.

¿Esto quiere decir, que hay contratar desarrolladores con conocimientos de testing? No necesariamente. 

El QDev tiene la responsabilidad de crear la cultura de testing, y por tanto de formar al equipo.

Cuando la responsabilidad del testing no es compartida, hemos visto cómo puede ocurrir que la productividad del equipo se vea mermada y por tanto la calidad se vea comprometida. 

Vamos a ver dos gráficos donde se muestra la tendencia del valor de calidad-productividad frente al coste de desarrollo de calidad. Es decir, cuánto cuesta invertir en desarrollo de calidad, frente al valor que nos aporta.

La línea verde muestra el coste de desarrollo de calidad del equipo.

La línea morada muestra el valor de calidad-productividad del equipo.

El primero muestra la tendencia en un equipo donde la responsabilidad no es compartida. El coste de inversión se mantendrá estático, y debido a que es probable que se formen cuellos de botella, el valor de calidad-productividad llegará un momento en el que irá descendiendo.

Coste de desarrollo de calidad vs valor productividad-calidad

El segundo muestra la tendencia en un equipo donde la responsabilidad es compartida y gestionada por un QDev. El coste de inversión será alto hasta que el equipo coja la dinámica y esté formado. A medida que aumenten las skills de calidad del equipo, el coste de desarrollo será menor, y el valor de calidad-productividad irá aumentando.

El trabajo del QDev consiste en ayudar a reducir el coste de desarrollo de calidad y una vez el equipo sea más autosuficiente, potenciar el valor de calidad-productividad.

Coste de desarrollo de calidad vs valor productividad-calidad

Conclusiones

Cuando trabajamos en la estrategia P&R es muy importante balancear la productividad del equipo y la inversión del desarrollo de calidad.

Crear una cultura y responsabilidad compartida de la calidad dentro del equipo ayuda a trabajar de forma más eficiente, pero requiere de uno skills de testing que es probable que el equipo no lo tenga en fases iniciales. 

La labor del QDev es velar por ello. Diseñar una estrategia de calidad que sirva como red de seguridad al equipo, ayudándoles a que trabajar de una forma más eficiente.

P&R es una estrategia viva y cambiante, por lo que es necesario monitorizar para ver en qué punto estamos en cada momento. Para ello será necesario definir unos KPIs que nos sirvan como punto de control.

En el siguiente capítulo veremos cuáles son los KPIs más importantes y cómo monitorizar la estrategia.

QDev, ¿Una nueva forma de aplicar el testing?

Introducción

Después de bastante tiempo sin publicar nada debido a que entre otras cosas he estado centrado en el proyecto de la comunidad de NorthemQuality, vengo con un artículo que me hace especial ilusión.

Durante estos dos últimos años, en el equipo hemos tenido que trabajar en varios proyectos que han tenido que hacer que el rol de QA/E tenga que evolucionar a uno nuevo que nosotros mismos hemos definido.

En este artículo me gustaría explicar qué es lo que ha hecho que el rol evolucione y en que ha evolucionado!

El proyecto como punto de inflexión

Todo comenzó cuando tuvimos que enfrentarnos a un proyecto en el que veíamos que íbamos a tener que lidiar contra bastantes infortunios.

El primero de ellos era que no teníamos un product owner para el proyecto por lo que, no íbamos a tener unas tareas bien definidas y por lo tanto, no íbamos a tener unos criterios de aceptación claros.

El segundo era que no había una estrategia de calidad, por lo que el testing que se hacía no era eficiente, y además era un stopper a parte de no tener unos resultados fiables.

Por otro lado, no existía el concepto de equipos multidisciplinares por lo que el testing se hacía por otros equipos que no fueran el equipo owner del proyecto.

Todo esto sumado a otros factores, hacía imaginarnos que la forma de trabajar de la que veníamos como equipo no iba a funcionar.

Nuestro equipo estaba compuesto por un Tech Lead, tres desarrolladores y un QA/E.

La evolución a QDev

Como QA/E era mi responsabilidad el que el producto a parte de responder como se requería. Antes de este proyecto, la forma en la que lo hacíamos era definiendo y desarrollando el juego de pruebas funcionales y mediante la gestión de bugs.

Pero claro… vista la situación a la que nos teníamos que enfrentar, no iba a funcionar.

El equipo siempre ha defendido el modelo estratégico “Prevention & Reaction” que concebí en su día, pero hasta el momento no lo habíamos podido poner en marcha. Igual era la hora de empezar a aplicarlo en la medida de lo posible y comprobar si era viable, había que arriesgar.

Lo primero que hicimos fue pensar como equipo en cómo queríamos trabajar el testing, que necesitábamos y en base a las necesidades, identificar cuál serían las responsabilidad del QA/QE.

Siempre he defendido que el QA/QE en los equipos multidisciplinares debe ser un desarrollador más, al igual que diferenciamos entre fronteder y backender.

De aquí nació el rol de QDev!

El nuevo rol, QDev, tiene que crear una red de seguridad de calidad (QSN) que brinde al equipo la confianza suficiente de desarrollar, para eso debía entre otras cosas debe:

  • Responsabilizarse de definir y aplicar la estrategia de calidad. En nuestro caso nos basamos en el modelo “prevention & reaction”, que va muy alineado al trabajo del QDev.
  • Definir y desarrollar el SUT (system under test) del producto.
  • Trabajar codo con codo con el product owner desde el inicio del proyecto en la definición de los criterios de aceptación. Diseñar el plan de pruebas de cada técnica de testing que posteriormente se desarrollará.
  • Desarrollar las herramientas y frameworks necesarios de testing.

Pero para esto, es muy necesario hacer un cambio de mentalidad y entender que:

  • El QDev es un desarrollador más, especializado en el desarrollo de la estrategia y arquitectura de calidad. Debe hacer partícipe al equipo en las decisiones que se tomen al respecto, siempre de forma consensuada.
  • Todo el equipo es responsable de la calidad del producto. Por ejemplo puede ocurrir que la gestión de los bugs se haga entre todo el equipo.
  • El QDev no es un juez, no dictamina si algo está bien o mal, el es el responsable de desarrollar la QSN que lo hace.
  • Las pruebas son una herramienta más, no son el objetivo.

Ejemplo de trabajo de QDev

Para poder entender mejor el concepto de cómo debería trabajar o cómo debería ser un QDev, vamos a ver un ejemplo.

Imaginemos que el equipo está compuesto por un Tech Lead, un Product owner y varios desarrolladores, entre ellos backenders, frontenders y un QDev (o más).

Desde el comienzo todos deberían participar en la definición del producto y en su arquitectura. El QDev en este punto debería comenzar a definir la estrategia de calidad:

  • Pensar en las necesidades de la QSN. La idea principal es que el equipo trabaje sobre una QSN existente u operativa.
  • Empezar a definir a alto nivel la arquitectura del SUT (system under test).

Supongamos que de ese trabajo, el QDev ha diseñado la siguiente estrategia que apoya su idea del QSN. Lógicamente esto es solo un ejemplo, muy discutible y dependiente del contexto del producto o proyecto.

De esta estrategia lo importante es entender que el QDev va a trabajar desde el comienzo del producto. Trabajará sobre las diferentes técnicas de testing que haya definido en las diferentes fases.

Siguiendo esta estrategia, el QDev no solo deberá centrarse en diseñar las pruebas e integrar las diferentes técnicas de testing, si no que, necesitará desarrollar el SUT aislado (mocks, contenedores etc) para que el equipo ejecute las pruebas de forma aislada. Incluso desarrollará herramientas para facilitar el trabajo del equipo.

Otro punto importante es no olvidarse de la importancia de seguir con el testing en la fase de producción.

Conclusiones

El evolucionar el rol de QA a QDev y comenzar a hacer uso del modelo “Prevention & Reaction” (al menos una pincelada) en nuestro caso fue un caso de éxito, pero todo depende del contexto del producto y de la madurez del equipo.

En mi caso fue muy importante la cultura del equipo y cómo entendíamos la calidad como una responsabilidad de todos. Es más, había tareas de QA que estaban diluidas en el equipo, yo entendía perfectamente que ellos harían pruebas funcionales, ya me encargaría yo de hacer el code review. Algo muy interesante es que también hacíamos pair testing.

Lo más importante es entender que el QDev, es el encargado de la QSN del producto o del proyecto. Para ello deberá tener los conocimientos suficientes para desarrollar y aplicar la estrategia de calidad, así como el SUT.

Ser el encargado no significa que sea el único responsable, como he comentado, la responsabilidad debe ser compartida por todo el equipo, por lo que es muy importante que todos participen en aplicarla.

El éxito depende de la unión del equipo, no del éxito de cada individuo.

Probe testing, the pathfinder

Introduction

A few days ago I was talking with a co-worker about a problem that his team is having.

The case is, this team consumes some APIs which contracts are changed without notifying the team and without any backward compatibility so, in the worst case, they get to the situation of having errors in production.

In an ideal world, the problem would be solved by applying contract testing or the API provider notifying about the changes, and developing always with backward compatibility, but what can we do when this is not happening?

Probe testing

We talked about the concept of using tests in the form of a probe or a pathfinder, in such a way that the tests inform us of any change in the API. In this case, I could think of several ways to probe:

Keep a scheduled test’s run, in such a way that when tests fail, we know that the contract has changed. The problem is that, as it is scheduled until it is executed and the probe detects the change, the problem could already be in production.

probe01

Keep the tests active, that is, have them running continuously. The problem that I see is that we would have a very extensive report and we could lose the real visibility of the status of the probe target. If for example, we add notifications, which would be the most logical thing to do, having a communication failure with the API we may start receiving false positive notifications. We would also have to handle the notifications.

probe02Have a listener. Imagine that changes are made in the repository of the API that we consume, if we have a service listening to this and we see or we are notified of a change, the probe will be launched to check the changes. Logically we should have view access to the repository, which is the case here, as it is part of the same company, or being a public repository.

probe03

The tests should be minimal and as simple as possible, focused on the problem, trying to avoid false positives. As a probe, we should not waste much time or resources in the maintenance. It would also be interesting to manage the probes through a dashboard: to see results, activate or deactivate them, etc.

Another interesting idea is that if we have the knowledge and resources enough, we can develop more intelligent probes, which manage their notifications system, execute, etc.

Intelligent testing, ¿Llega una nueva era?

Introducción

A través de un tweet de Lisa Crispin, llegue a un artículo del blog de Mabl, en el que hablaban un poco sobre el concepto de “intelligent testing”.


En este artículo me gustaría repasar los cuatro principios que se tratan del Intelligent testing. Si bien es cierto que el video del blog habla sobre cómo lo aplican a través de Mabl, intentaré ser agnóstico al producto.

¿Qué intenta resolver “Intelligent testing”?

Para entender el concepto de intelligent testing, creo que es necesario conocer los “defectos” del testing convencional (vamos a llamarlo así).

En mi opinión el testing convencional estaba pensado y muy acoplado a las metodologías de desarrollo convencionales. Con la entrada de las metodologías ágiles, el testing ha sufrido ciertos cambios intentando adaptarse, pero no siempre con el éxito que debería.

Esto ha provocado que el testing llegue a ser el cuello de botella del sistema de CI/CD. Para evitarlo y poder implementar el testing dentro de las metodologías ágiles, en muchas ocasiones se ha tenido que crear estrategias de testing muy acopladas al sistema de CI/CD, lo que tampoco es del todo correcto.

Creo que la evolución del testing siempre ha estado por detrás del desarrollo, cuando debería ir de la mano. También creo que gran parte de la culpa la tenemos los testers y QA/QEs que nos hemos ido adaptando al código y a la tecnología, en vez de estar a la altura. Debemos ser más preventivos y menos reactivos. Todavía nos queda mucho por aprender.

Lo que intenta el “testing inteligente” es básicamente eso, dejar que el testing sea el cuello de botella de nuestro desarrollo, y para eso se basa en cuatro principios.

1.º principio: Adaptarse a los cambios

Uno de los grandes problemas de la automatización, son los falsos positivos. Cuando lanzamos la suite de pruebas podemos encontrarnos con una gran número de fallos que tendremos que analizar, ya que seguramente un porcentaje de los fallos sea debido a falsos positivos.

Algunos falsos positivos vienen dados por cambios en el código que hacen que nuestro test falle, sin que en realidad sea un fallo de la aplicación. Esto suele ocurrir mucho en el caso de las pruebas de UI.

En las pruebas UI, nuestro test está acoplado a los componentes con los que interactúa, lo que hace que el test sea altamente sensible. Cuando por alguna razón el xpath o el selector del componente cambia, nuestro test falla. No quiere decir que sea error de la aplicación, es un caso de falso positivo.

Tenemos que adaptar manualmente el test y volverlo a lanzar. Esto supone una inversión de tiempo alto, por lo que somos un cuello de botella.

Lo ideal sería que el framework de testing sea lo suficientemente inteligente como para identificar el problema (un cambio del selector, en el caso anterior), identificar el cambio, probarlo y hacer los cambios en el test, de las forma que las siguientes ejecuciones vayan correctamente.

Claro, esto es relativamente “fácil” en las pruebas de UI. Pero qué ocurre si realizamos otro tipo de pruebas funcionales o de performance (porque recordemos que podemos integrar las pruebas de performance dentro de nuestro CI).

En estos casos no lo veo tan claro que sea tan fácil de adaptar el test. Supongamos que realizamos pruebas funcionales de API, lanzamos las pruebas y fallan debido a un cambio de contrato. Entiendo que si tenemos los contratos definidos y actualizados o hacemos uso de contract testing, podremos adaptar nuestros tests basados en esas fuentes de conocimiento.  

En el caso de las pruebas de rendimiento, debería depender de qué tipo de pruebas o sobre que se lancen (UI, API etc.), y adaptarse.

Otra opción que se me ocurre sería disponer de un sistema inteligente que “analice” los cambios y notifique o haga los cambios pertinentes en las suites de testing, sea del tipo que sea, funcionales, performance…

2.º principio: Testing desde el Cloud

 

Otra forma de reducir el tiempo de ejecución de las pruebas es la ejecución en paralelo. Las plataformas Cloud nos facilitan este hecho, ya que podremos disponer bajo demanda o escalar el número de ejecuciones y entornos sobre los que queremos lanzar las pruebas.

En el caso de las pruebas funcionales podremos reducir considerablemente el tiempo de ejecución, debido a que dividiremos las pruebas por el número de “ejecutores”.

En el caso de las pruebas de rendimiento podremos aumentar la carga o el número de usuarios virtuales sin penalizar el gestor de carga.Al lanzarlo sobre sistemas cloud podremos beneficiarnos de los servicios de estas plataformas, como sistemas de análisis (para analizar los resultados), heurísticos etc..

Cloud testing

 

3.º principio: El output debería ser más afirmativo y resolutivo

Como he comentado en el primer principio, tras terminar las pruebas, hay una fase de análisis de resultados. En muchas ocasiones (por no decir en la gran mayoría), cuando hay fallos, los resultados no son del todo claros y nos toca invertir tiempo en ver cual es el problema.

Esta inversión de tiempo, una vez más, nos lleva a ser un cuello de botella para los resultados, y en otras ocasiones a que se nos escapen bugs, debido a la falta de atención que ponemos en su resolución.

El que las pruebas den un output con la información necesaria (ni exceso, ni defecto) y esa información llegue a ser resolutiva, es tan importante como unos buenos teses. De nada sirve tener un test perfecto, si los resultados no son lo suficientemente informativos.

Hoy en día sacamos trazas (en ocasiones excesivas) y screenshots (en ocasiones confusos), pero no es suficiente para reducir el tiempo de análisis de los resultados.

Bajo mi punto de vista, tenemos que invertir muchos más esfuerzos en mejorar este punto. La parte buena es que los sistemas heurísticos y de machine learning cada vez están más avanzados, y creo que de alguna forma podremos integrarlo con el testing.

Creo que sería interesante disponer de un output inteligente, donde en base a los resultados obtenidos, comparándolo con una fuente de conocimientos de otros resultados anteriores y los cambios realizados, podamos dar unos resultados, no solo con buena información, si no, con el problema detectado y parte de su resolución.

4.º principio: Testing integrado en el CI

Este último principio creo que es el que está más integrado. Actualmente hay pocas empresas que no tengan el testing integrado en el sistema de CI/CD. Ya son muchos sistema de CI que disponen de plugins para muchos frameworks de testing.

El problema es que si no se cumplen los otros tres principios, el testing seguirá siendo el palo en la rueda del CI.

Conclusiones

El testing inteligente es un muy buen concepto que acerca cada vez más la posibilidad de estar a la altura de la evolución del desarrollo, creo que todavía queda por evolucionar varios aspectos, pero es factible, lo que es muy buena noticia.

Parte de esa responsabilidad recae en los testers y QA/QEs que creo que necesitamos una evolución como concepto y rol. No podemos seguir anclados en el probar el software, ni en la idea idílica de “garantizar la calidad”.

La tecnología de hoy en día, el cloud, la heurística, nos permite dar un paso más adelante y ayudarnos a anteponerse a los errores, más que encontrarlos.

Model based testing mediante Graphwalker

Introducción

Llevo un tiempo investigando nuevas formas para definir y diseñar las pruebas en nuestro equipo. La idea principal es disponer de un modelo de diseño de pruebas que sirva como punto de encuentro entre el rol de producto, desarrollador y QE.

En este post vamos a hablar sobre Model-based testing o MBT que se nos ayuda a acercarnos bastante a la idea que he comentado arriba.

Y como siempre, vamos a verlo en práctica, para ello vamos a hacer uso de Graphwalker, una herramienta de MBT.

Podéis descargar el proyecto desde mi Github. Esta vez, veréis que el proyecto únicamente tiene logs para ver el flujo de secuencia del test. Usarlo como base y experimentad!

Model based testing (MBT)

En Model-based testing (MBT) se define nuestro SUT en base a vértices o vertex y la transición o edge entre estos.

model based testing example

El modelo, se compone de tres vértices o vertex:

  • v_App_closed, como vértice inicial y final.
  • v_App_running.
  • v_Display_preference

De tal forma que en el caso de este modelo (que es sencillo) si queremos probar el 100% de los vértices y las transiciones, el test path más lógico sería:

Que para llegar de uno a otro requiere de ciertas transiciones o edge:

  • e_Start_app, transición inicial.
  • e_Close_app.
  • e_Open_preferences.
  • e_Close_preferences.

De tal forma que en el caso de este modelo (que es sencillo) si queremos probar el 100% de los vértices y las transiciones, el test path más lógico sería:

mtb example flow

Como hemos dicho, nuestro caso es muy sencillo y es fácil identificarlo, pero lo normal es tener modelos mucho más complejos.
Para estos casos es cuando las herramientas de MBT como Graphwalker, nos ayudan a generar los test path.

Normalmente las herramientas MBT nos proporcionan dos formas de generar los test path.

OFFLINE

En la generación offline, los test path se generan previamente, de tal forma que puedan ser ejecutados con posterioridad por la herramienta.

ONLINE

En la generación online, los test path se generan automáticamente en tiempo de ejecución. Para este caso, la herramienta o el framework de MBT estará acoplado al código del test, pero esto nos aporta ciertas ventajas.

Vamos a verlo en la práctica!

Añadir Graphwalker a nuestro proyecto

Para añadir Graphwalker a nuestro proyecto, únicamente necesitamos añadir las dependencias y el plugin:

Lo veremos más adelante, pero mediante maven generamos las interfaces que tendrán que implementar nuestras clases.

Diseñando el modelo

Para diseñar el modelo vamos a usar yEd, que nos permitirá diseñar el modelo gráficamente, siendo totalmente compatible con Graphwalker.

Imaginemos que tenemos una aplicación, de venta de entradas. Formamos parte del equipo de pagos y tenemos una arquitectura basada en microservicios.

La idea es probar la feature de la compra de una entrada a través de las APIs.

mbt purchase

Nuestro modelo se va a componer de cinco vertex:

Crear usuario -> Añadirle el medio de pago -> Crear el carrito de compra del usuario -> Añadir la entrada al carrito de compra -> Comprar el ticket

Si nos fijamos en los edges entre los vértices, podemos ver pueden ir acompañados de “cierta lógica”.

Es muy importante nombrar los edged, con una “e” por delante y los vertex con una “v”, de esta forma Graphwalker podrá identificarlos.

mbt exa 1

Por ejemplo, en el caso del edge e_AddPaymentMethod, vemos el código:

/paymentMethod=’paypal’; validPaymentMethod=true;

Esto quiere decir que Graphwalker, cuando vea el símbolo /, todo lo que venga posteriormente lo va a interpretar como código javascript, es decir, en esta transición vamos a setear valor para dos variables.

mbt ex 2

Si nos fijamos en el siguiente edge, vamos a ver un código un poco diferente:

[validPaymentMethod]

Graphwalker cuando vea los símbolos […], lo va a interpretar como un condicional, es decir, va a comprobar que el valor de validPaymentMethod sea true.

Al ser código javascript, tanto el código como las condiciones pueden ser mucho más complejas, depende de las necesidades de nuestras pruebas.

Os dejo un enlace de la documentación de Graphwalker donde podreis verlo con mucho más detalle.

GENERANDO LA INTERFAZ DEL TEST

Para generar la interfaz del modelo para nuestras pruebas, vamos a guardar el fichero graphml de yEd en el directorio src/test/resources/ del proyecto.

Ejecutamos el comando de maven:

mvn graphwalker:generate-test-source

Esto nos generará en el directorio target la interfaz que tendremos que implementar en nuestras pruebas para lanzarlo con Graphwalker:

interface

Desarrollando y ejecutando las pruebas

En el test definiremos el algoritmo con el que queremos que se genere la secuencia de nuestro test.  La interfaz nos indicará los métodos de los que se compone nuestro test.

En el test definiremos el algoritmo con el que queremos que se genere la secuencia de nuestro test. En nuestro caso:

@GraphWalker(value = "quick_random(edge_coverage(100))", start = "e_CreateUser")

  • quick_random: Ejecutará el camino más corto, lo va decidir mediante el algoritmo de Dijkstra.
  • edge_coverage: Que ejecute el 100% de los edges que hemos definido en el modelo.

Es decir, en nuestro caso va a ejecutar todos los edges, y por cada vuelta va calcular cual es el camino más rápido.

Esta parte es la más interesante e importante, debido a que depende el algoritmo de ejecución que definamos probaremos más o menos, es decir, decide nuestra estrategia de testing.

Os dejo un enlace donde podéis ver las diferentes opciones de ejecución.

Para ejecutar las pruebas:

mvn graphwalker:test

test results

Como resultado vemos que se han ejecutado el 100 de los vertex y los edges, por lo que hemos lanzado pruebas con una cobertura del 100%.

Conclusiones

Definir las pruebas con MDT puede ayudarnos a alinearnos mejor con producto y con desarrollo, dado que los modelos clarifican bastante el flujo de testing de nuestro SUT.

El poder integrarlo con herramientas como Graphwalker en donde podemos definir el algoritmo de ejecución de las pruebas, nos aporta una gran flexibilidad a nuestra estrategia de testing.

Imaginemos que queremos hacer smoke testing, bastaría con definir la ejecución como:

e_start(reach_vertex())

Y si por ejemplo queremos hacer pruebas de estabilidad podemos añadirle tiempo de duración a nuestras pruebas:

random(time_duration())

Otro de los puntos fuertes, es que no está casado con ningún tipo de testing, es decir, al definir únicamente el flujo o los pasos del test, podemos realizar desde test de UI mediante selenium, a APIs, performance etc.

Testing with sewage treatment plant

Imagine that an issue in development is like water that has to be purified, and our testing architecture or techniques are the purification machine.

If we do not purify or filter, the water will come with a big amount of waste. In order to have clean water to consume, it must go through a process of “filtering” step by step.

filter

The same happens with the issue that we are developing. If we do not apply testing techniques and clean code, we will obtain a product with many residues (bugs, corner cases, dirty code, etc).

But we have to apply them from an early stage. So it is not correct doing testing once the issue is developed. The testing starts from the very beginning, defining the acceptance criterias, applying the different testing techniques during the development process.

In this way we will detect the “imperfections” in advance, and the delivery and deployment will be safer and faster avoiding the testing bottleneck.

A bit of dirty water can contaminate the entire pool in production, after that, ¿How could you find the drop that spoiled it all?