Abstracta Webinar: Diseñando una estrategia preventiva mediante pruebas aisladas e integradas

Introducción

A últimos del mes pasado fui invitado por Abstracta para participar dando un webinar en sus tech talks. Aquí os dejo su canal de YouTube.

La verdad es que fue una experiencia muy agradable, por varios motivos. El primero de ellos, es por poder participar con grandes amigxs que me han apoyado en la creación de este blog, Federico Toledo, participando incluso con algún post, o que han participado en la comunidad de NorthemQuality como Lisandra Armas.

Otro de los motivos es por poder presentar en la charla y con ejemplos, lo que os llevo hablando en este blog durante un tiempo. En una hora no da tiempo de hablar de todo lo que me gustaría, pero sin duda me quedé más que satisfecho. Siempre se escapa algún efecto demo, pero lo salvamos bien 😜. Así que os lo agradezco de ❤️.

Se que quedan muchas cosas por investigar y por crecer en este area de las estrategias preventivas y las pruebas aisladas, pero hay que dejar espacio a otros temas y no monopolizarlo. Ahora toca investigar otras cosillas para el blog, pero eso no quita que de vez en cuando escriba algo al respecto!

Por último os dejo abajo el video de la charla y el enlace a la presentación y repo!

https://slides.com/aaguila/designing-a-preventive-strategy#/

https://github.com/QAJungle/isolated-test-examples

Video

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.

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.

Mountebank y sus impostores, aislando nuestras pruebas

Introducción

En el blog hemos hablado en varias ocasiones sobre cómo mockear las APIs, teniendo en cuenta que es una parte importante para aislar nuestras pruebas en las arquitecturas basadas en microservicios.

Si bien es cierto que haciendo uso de contract testing podemos aislar nuestras pruebas y comprobar el correcto funcionamiento de nuestros servicios y los clientes, en ocasiones podemos necesitar llegar más lejos.

Pongamos un caso real. Hace unos meses que con mi equipo trabajamos en un proyecto en el que era necesario comunicarse con un sistema tercero asíncrono.

A nivel de pruebas lógicamente no podíamos acoplarnos a ese servicio, por lo que decidimos simularlo de alguna forma (teniendo en cuenta las restricciones tecnológicas que teníamos), es más, otros equipos necesitaban de ese servicio para sus pruebas (negocio, financiero, performance).

Por este motivo nos decantamos por desarrollar un servicio interno que simulara la respuesta del servicio externo, de tal forma que todos los equipos involucrados en el proyecto podrían utilizarlo. Con contract testing, este caso no podría ser.

Y aquí, es donde entran los “test doubles” y mountebank, una herramienta que nos va a permitir generar test doubles multi-protocolo. Gracias a Alvaro Salazar que me recomendó darle un vistazo.

¿Qué son los test doubles?

De forma sencilla, podemos decir que un test double es una copia o imagen de la respuesta y el comportamiento de un servicio.

En el caso de mountebank disponemos de dos tipos de test doubles: mocks y stubs. Dependiendo del tipo de pruebas que queramos hacer usaremos uno, otro, o los dos.

Con los mocks podemos comprobar que la llamada se ha hecho confiando en la respuesta del servidor, mientras que los Stubs, en base a una request nos devolverán una response, es decir, definimos su contrato.

¿Cómo funciona Mountebank?

Al contrario de otras herramientas, uno de los puntos fuertes de mountebank es la sencillez de generar los test doubles.

mountebank

Mountebank levanta un servicio que se encuentra a la escucha de que el cliente le mande el contrato para crear el test double, o impostor como también lo llaman. Este creará el impostor con los datos de configuración que se detallan en el contrato (protocolo, endpoint, puerto, petición, respuesta, comportamiento etc.)

La aplicación ya podrá consumir del test double, en vez del servicio real en el tiempo de test, aislando así nuestras pruebas.

Una buena estrategia es, tal como dicen en la página de mountebank, que en la preparación del entorno de pruebas, las pruebas sean las encargadas de enviar los contratos a mountebank para generar los test doubles, y cuando finalicen, sean las encargadas de comunicar que se eliminen. De esta forma todo quedará en tiempo de pruebas.

Otra forma de generar los test doubles, es mediante el uso del proxy que nos provee el servicio de mountebank. Mediante el proxy, mountebank capturará las llamadas reales que se hacen al servicio externo y creará los stubs en base a las peticiones y las respuestas que ha capturado. Lo veremos más adelante en otro post.

Un ejemplo sencillo de aislamiento

En este caso he preparado un proyecto muy muy sencillo, que podreis ver en github. En el proyecto vamos a probar la clase BookGateway, que es la encargada de consumir un servicio externo.

En este punto vemos que si nuestra intención es aislar nuestras pruebas de integración, vamos a tener que hacer un stub de ese servicio. Teniendo en cuenta que disponemos de la información, vamos a desarrollar el contrato:  

La idea es que el stub se gestione en el tiempo de pruebas, es decir, que cuando se lancen las pruebas se genere el stub, y cuando se terminen, se destruya. Para ello, vamos a  hacer uso de un cliente java de mountebank, javabank:

En el test, en el método @beforeAll vamos a crear el impostor cargandolo desde el fichero de configuración para que cada vez que se ejecute cree el impostor del servicio de book.

En el método @afterAll eliminamos todos los impostores, de tal forma que el entorno quede limpio para las siguientes ejecuciones.

El test es muy sencillo, simplemente llamamos al método getBook del gateway, el cual hará la petición al impostor en vez de al servicio externo.

Ahora solo nos queda instalar el servicio de mountebank y levantarlo, lo podéis ver en el siguiente enlace, y ejecutar las pruebas.

console

test results

El ejemplo que os he presentado es muy sencillo, con una configuración muy básica, pero mountebank nos permite generar stubs y mocks mucho más complejos, con diferentes requests y responses, así como definir diferentes comportamientos. En su documentación podéis verlo con mucha más profundidad.

Conclusiones

El disponer de test doubles (ya sea con mountebank u otra herramienta) puede ser un arma muy interesante en la batalla del testing de microservicios.

En mi opinión es una utilidad complementaria a contract testing y al uso de por ejemplo testcontainer como estrategia para llegar a tener unas pruebas determinísticas.

Uno de los beneficios de utilizar un servicio de test doubles, es que podemos utilizarlos por más de una aplicación y por más de un objetivo. Podemos tener el servicio de mountebank levantado y que diferentes pruebas consuman sus stubs. Bien es cierto que esto no es muy recomendable ya que perdemos ciertos beneficios del aislamiento ya que los compartimos, pero dependiendo del caso, tendremos la opción.

El beneficio claro de Mountebank es que es multiprotocolo, por lo que no solo nos va a proveer stubs o mocks HTTP, si no que, en el caso de querer mockear un servicio de correo SMTP o un servicio TCP también podremos. En nuestro caso hemos utilizado el cliente java, pero aquí tenéis los diferentes clientes para otros lenguajes.

Todavía me queda por investigar, es más, creo que es una herramienta que en el caso de mi equipo puede sernos de utilidad, así que os iré informando!

 

 

 

Aislando nuestras pruebas con Testcontainer

Introducción

Hace unos días escribí sobre las pruebas aisladas e integradas. Uno de los mayores retos en nuestra estrategia de testing es el hecho de intentar aislar nuestras pruebas.

Hemos visto cómo aislarlas mediante contract testing, pero; ¿Cómo aislamos nuestras pruebas de integración o las funcionales?

En este post vamos a hablar sobre Testcontainer, una librería de java que nos provee instancias ligeras de cualquier contenedor de Docker (bases de datos, navegadores, servicios etc.).

Como siempre vamos a ver los ejemplos con un proyecto que podréis descargar desde GitHub.

Vamos a ello!

Aislando las pruebas del proyecto

App architecture
El proyecto de ejemplo es sencillamente una aplicación conectada a una base de datos MySQL y que consume a encoder-service,  un servicio propio, que es usado por todas nuestras aplicaciones.

Vamos a desarrollar pruebas de integración. Comprobaremos que nuestra aplicación comunica correctamente tanto con la base de datos, como con el servicio.

Mediante Testcontainer cuando ejecutemos las pruebas, se levantará un contenedor Docker de MySQL y otro de encoder-service.

Una de las preguntas que os podéis hacer, es el porqué levantar un contenedor del servicio, si lo podemos mockear. Hay ocasiones en las que cuando realizamos pruebas, necesitamos recibir un comportamiento real, y no uno esperado. Este ejemplo pretende acercarse a esa realidad.

Test architecture

Añadiendo Testcontainer al proyecto

Vamos a añadir dos dependencias a nuestro proyecto. Las dependencias generales de Testcontainer, y la dependendencia de testcontainer de MySQL.

Además de añadir las dependencias de Testcontainer, deberemos tener las dependencias del conector de MySQL y Spring data.

Pruebas de integración sobre MySQL Testcontainer

Lo bueno de utilizar Spring Data JPA,  es que nos facilita la implementación y por tanto el uso de la capa de datos. Testcontainer se adapta muy bien a Spring Data JPA debido a que nos provee un driver jdbc que gestiona el contenedor de base datos.

Lo que nos interesa es ejecutar el contenedor de MySQL en tiempo de test, por lo que la configuración la vamos a añadir en el directorio test.

En la configuración de JPA, es importante destacar la propiedad de ddl-auto. El valor que vamos a indicarle es create-drop, de esta forma cuando se instancie el contenedor, se creará la base de datos con nuestra entidades.

Para las pruebas vamos a añadir unos datos iniciales a la base de datos, por lo que cuando se creen las tablas, se ejecutará el fichero data.sql. Para ello es importante que la propiedad initialization-mode tenga el valor always, en la configuración de datasource.

Lo más importante es driverClassName, donde indicaremos el driver de Testcontainer. En la propiedad de la url de conexión, el hostname, el puerto y el nombre de la base de datos van a ser ignorados.

El test que es muy sencillo, únicamente comprueba que se almacena y se leen los datos de la base de datos. Lo importante es sacar la conclusión de que el uso de testcontainer es totalmente transparente para el test, es decir, el test no sabe que está ejecutando las pruebas contra un contenedor de MySQL. Por lo que sí tenemos la necesidad de cambiar de base de datos o de versión de la base de datos, nuestras pruebas no se van a ver afectadas.

testcontainer results

 

Pruebas de integración sobre enconder-service Testcontainer

Primero, hemos tenido que “dockerizar” el encoder-service y añadirlo a nuestro local registry. Ahora en la configuración test de nuestra aplicación vamos a añadir el fichero docker-compose, con la configuración mínima para instanciar el contenedor de nuestro servicio.

En este caso, nuestro test si va a estar acoplado a las dependencias de Testcontainer, debido a que tenemos que hacer uso de docker compose.

Como hemos comentado, dado que queremos instanciar el contenedor de nuestro servicio desde el fichero de docker-compose.yml, vamos hacer uso de DockerComposeContainer, indicando el nombre del servicio y el puerto. En el test, obtenemos la url y el puerto del contenedor mediante getServiceHost y getServicePort. Realmente es muy sencillo.

testcontainer result encoder service

Conclusiones

Testcontainer nos permite aislar nuestras pruebas de forma muy sencilla. Mediante Spring data JPA y Testcontainer nuestras pruebas de integración de base de datos estarán totalmente desacopladas.

La mayor pega puede ser el hecho de que si queremos hacer uso de Docker compose, nuestras pruebas no van a disfrutar de la virtud de estar desacopladas de Testcontainer.

Al aislar las pruebas reducimos el tiempo de prueba, debido a que no necesitamos hacer conexiones reales y reducimos el número de falsos positivos por fallos en el otro extremo.

El sistema de CI/CD se puede ver muy beneficiado, dado que no vamos a necesitar desplegar todos estos servicios, BBDD etc. en nuestro entorno, por lo menos en entornos previos al stage o preproducción, y los desarrolladores podrán probar con mayor fiabilidad su código.

Además, Testcontainer nos proporciona librerías para hacer pruebas de UI, por ejemplo con Selenium. Y bueno… como habéis visto en el caso de encoder-service, cualquier aplicación dockerizada se podrá instanciar con testcontainer.

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?

Charlando de testing con: Federico Toledo

Introducción

Si hay algo que me gusta y de lo que creo que aprendo es de hablar con los compañeros sobre el mundo del desarrollo y el testing. La mayor parte del conocimiento que tengo hoy en día es gracias a leer artículos y a discutir ideas.

Como creo que es mejor compartir, he decidido crear una nueva serie en el blog en la que colgaré “entrevistas” o “momentos de charla” sobre testing con otros compañeros. Mi idea es hablar sobre testing no solo con testers o QEs, si no con todos los roles implicados en el mundo del desarrollo. Conocer diferentes opiniones y puntos de vista que puedan hacer discutir y replantearme las cosas.

En el primer post de la serie “Charlando de testing con”  lo he decidido hacer con Federico Toledo, tras hablar varías veces con el y leer su libro creo que tiene una opinión muy interesante sobre el actual estado del testing, así como grandes ideas. No me gustaría adelantaros nada así que os presento la charla con Federico Toledo:

Entrevista

 

QA Jungle – Charlando de testing con Federico Toledo from QA Jungle on Vimeo.

Comentarios del libro: Introducción a las Pruebas de Sistemas de Información

Introducción

tapa libro
Hace unos días Federico Toledo escribió un nuevo post en su blog hablando sobre el libro que escribió en el 2014  “Introducción a las Pruebas de Sistemas de Información“. Os recomiendo que os descarguéis el libro gratuito en el mismo post.

En la siguiente lectura voy a tratar de comentar y hacer crítica de lo que me gusta y lo que me gustaría encontrar en el libro. Del porqué recomiendo el libro y en mi opinión a qué tipo de lector o profesional está orientado.

¡Vamos allá!

 

 

El autor

Para mí es muy importante saber quién es el autor de un libro, sobre todo si es técnico. La razón es que muchos libros técnicos comparten el mismo contenido, hablan de los mismos temas y tratan los mismos conocimientos.

¿Entonces donde se encuentra la diferencia? En el autor. El autor aporta su experiencia, sus conocimientos y su personalidad. Eso es lo que hay que buscar en los libros técnicos. Tenemos que mirar más allá de la técnica pura y dura, tenemos que analizar la experiencia del autor sobre el tema que trata. Si no, solo se quedará en un simple manual.

A Federico Toledo le conozco de hablar personalmente alguna vez con él, de leer sus artículos y seguir sus consejos. Tiene un CV y una experiencia envidiable, pero sin entrar a lo personal, es una persona que se centra mucho en la práctica, al menos en los artículos que he leído. Por lo que antes de comenzar a leer el libro me hacía una idea de cómo se iba a enfocar.

Enfoque del libro

Uno de los mayores problemas de los libros técnicos, es la densidad a la hora de leerlos. ¡La mayoría se hacen eternos!

En mi caso ocurre por varios motivos: El autor es muy monoto, el libro no aporta mayor información que otros, no encuentro en el libro lo prometido. Introducción a las Pruebas de Sistemas de Información no ha sido el caso.

He leído varios libros y artículos de testing de diferentes autores y muchos de ellos solo han sido copias de otros, algunos ni siquiera aportaban la experiencia propia. Con lo que al final pensaba que solo aportaban humo.

En el caso de Introducción a las Pruebas de Sistemas de Informacióha sido diferente. Tenía ganas de encontrar un libro que se centrará en la práctica, que me aportara técnicas y estrategias para mejorar mis skills de testing. Bien es cierto que no profundiza demasiado en algunos de los conceptos tratados, tampoco creo que sea ese su objetivo, ya que es un libro de introducción al mundo del testing.

Temas del libro

¿El libro cubre temas suficientemente atractivos para mí? La respuesta es sí.

Cuando leemos libros de testing, nos encontramos con muchos temas sobre cómo implementar el testing en la metodología X. Al final nos encontramos que se han tratado más temas de metodologías y gestión de equipos que de estrategias o técnicas de testing.

Introducción a las Pruebas de Sistemas de Información, en mi opinión toca los temas necesarios para entender y comenzar a aplicar las estrategias y técnicas de testing. Creo que el autor ha buscado ser claro y practico con los temas, y creo que lo ha conseguido.

El que haya una temática tan clara ayuda a que también sea un libro de consulta, me explico. Muchas veces lees un libro técnico y tocas mucha información en varios temas, como si de una novela técnica se tratase. En el caso de Introducción a las Pruebas de Sistemas de Información si necesitas información sobre pruebas de rendimiento, basta con el ir al tema “Pruebas de Performance” para encontrarte toda la información necesaria, sin necesidad de bucear por todo el libro.

Lo que me gusta y lo que me gustaría encontrar


LO QUE ME GUSTA

  • Es un libro muy práctico, aportando ejercicios, lo que ayuda a entender y a interiorizar los temas que se tratan.
  • Para apoyar los ejercicios y las prácticas aporta herramientas al lector. Si de verdad te interesa, no te vas a quedar con la miel en los labios, puedes practicas e incluso utilizarlas para tu día a día en el trabajo.
  • El autor expone casos cotidianos (fuera de toda técnica) y humanos para entender los ejemplos de testing. Esto es muy positivo sobre todo para las personas que comienzan o quieren entender en el mundo del testing.
  • Repasa muchas técnicas de testing y la opinión personal respecto a cada una, con lo que aparte de aportar conocimientos técnicos, también aporta personales. ¡Lo que yo busco en un libro!
  • Aporta diferentes artículos y opiniones respecto a varios temas que trata. Creo que el autor buscar que el lector saque su propia conclusión a través de varios puntos de vista, algo muy positivo ya que ayuda a que el lector pienso por si mismo.

LO QUE ME GUSTARÍA ENCONTRAR

  • Cuando el autor habla de las técnicas de testing, echo en falta tratar la teoría de la pirámide del testing . Durante el tiempo que llevo trabajando como ingeniero de calidad me he encontrado con pirámides invertidas, lo que es signo de no aplicar y desarrollar correctamente el testing. Echo en falta que se trate este tema, ya que creo que es necesario conocerlo y entenderlo para no acabar con suites inmanejables y poco eficientes. No obstante, Federico Toledo si lo ha tratado en la versión en inglés del libro :).
  • Una vez más echo de menos que se traten temas de estrategias y técnicas de prevención. Es cierto que hoy en día el mundo del testing y el QE está orientado a garantizar la calidad mediante diferentes técnicas de pruebas (funcionales, performance, automáticas, unitarias…). Pero creo que sería interesante concienciar en acto de la prevención.
    Estás mismas técnicas pueden ser usadas para prevenir la inserción de fallos y la regresión de calidad, más que buscarlos. Poco a poco se está invirtiendo más tiempo en investigar técnicas de prevención de bugs mediante algoritmos de IA etc.
    Lo más seguro que no sea el “scope” del libro, pero suelo echar en falta algún tema o comentario al respecto, que muestre que el mundo del QE intenta expandirse y evolucionar también hacia otros aspectos.

Conclusión

Tanto si quieres comenzar o entender en el mundo del testing, como si eres un experto y quieres afianzar tus conocimientos te recomiendo la lectura del libro.

En el primer caso vas a ganar los conocimientos suficientes para entender la importancia del testing y comenzar a desarrollar tus habilidades.

En el segundo caso puede que te encuentres que el autor no profundiza, pero hay que entender que no es el “scope” del libro. A pesar de ello vas a poder descubrir y repasar técnicas muy interesantes, así como ver otros puntos de vista que seguramente van a hacer que te lleves una grata sorpresa.