Mutation testing – PIT nuestro gran amigo

Introducción

No siempre las pruebas unitarias que realizamos dan la cobertura suficiente a nuestro código, no nos engañemos. Allá por los años 70-80 se creo un nuevo concepto de testing denominado mutation testing. Consistía en modificar ciertas lineas de nuestro código, para posteriormente probar si en realidad fallaba o no.

futurama_mutantesEstos cambios en el código se denominan mutantesy como si de un juego se tratará, debemos matarlos, es decir, si los teses fallan, se dice que han matado a los mutantes. Cuantos más mutantes matemos, mejor.

En este post vamos a ver como se realiza mutation testing mediante PIT, una herramienta que se va a encargar de realizar las mutaciones y ejecutar nuestros teses unitarios para ver si logra matar todos los mutantes.

Para ello, he dejado en mi github el proyecto con el que he realizado las pruebas. Podéis descargarlo y jugar a matar mutantes 😉

El código es “refactorizable”, lo sé, pero lo que he intentado es disponer de código sencillo que nos permita jugar con las pruebas unitarias y las mutaciones, lo vamos a ver.

Mutación del código

Podéis ver todos los tipos de mutaciones que realiza PIT en el siguiente enlace, pero vamos a ver una mutación sencilla, para ayudarnos a entender mejor lo que son las mutaciones en código.

La mutación de limites condicionales, consiste en cambiar los operadores relacionales de nuestras sentencias. Así comprobaremos que nuestras condiciones están bien construidas. Lo vemos en la siguiente tabla:

Original conditional Mutated conditional
< <=
<= <
> >=
>= >

En nuestro código tenemos el siguiente condicional:

De forma que la mutación condicional que se hará será:

Cuando ejecutemos las pruebas de mutación, PIT se encargará de realizar todas las mutaciones. Por defecto se aplican las mutaciones básicas, pero si queremos ser más especificos o llegar un mayor nivel de mutación, PIT nos ofrece una lista de mutaciones que tendremos que activar por configuración.

Lanzando las pruebas de mutación

A pesar de que las pruebas de mutación nos ayudan a detectar errores en nuestro código, también son muy útiles para comprobar si nuestras pruebas unitarias son correctas o garantizan la cobertura necesaría, por lo que son un gran apoyo.

Veamos la lógica de nuestro código y las pruebas unitarias que garantizan que el código funcione correctamente. Para el producto que queremos comprar, se comprueba que haya la cantidad disponible y en caso correcto se decrementa en 1 el valor de la cantidad y se devuelve. Sencillo.

Nuestras pruebas unitarias comprobaran que se descuentan correctamente los productos y que no podemos comprar más productos de los que dispone la máquina.

Si ejecutamos la pruebas unitarias, nos darán un resultado verde, es decir, todo funcional correctamente! (Según nuestras pruebas)

Unit testing result

Ahora es el turno de lanzar nuestras pruebas de mutación. Para ello, nos situamos en la raiz del proyecto y ejecutamos:
➜ mutation-testing git:(master) ./gradlew pitest


pit coverage

Las pruebas generan un reporte que se almacena en {project}/build/reports/pitest/. Vamos a analizarlos.

En nuestro proyecto de pruebas tenemos dos clases en src/java. En este caso la clase ProductCode es un enum, por lo que vamos a necesitar cobertura de test.

La lógica se encuentra en la clase VendingMachine.java, que es la encargada de gestionar la tienda.

 

En nuestras pruebas unitarias pensabamos que podríamos cubrir todos los casos, pero PIT nos muestra que no es así. Vamos a ver la razón. Las lineas verdes indican que PIT ha matado a los mutantes, mientras que las lineas rojas nos indican que los mutantes han sobrevivido.
pit coverage

Al fijarnos en la lista de mutaciones, vemos que en las líneas 20 y 27 se han realizado dos mutaciones de límite condicional que no han pasado las pruebas.
pit coverage

Si nos fijamos en el código de nuestras pruebas unitarías, vemos que en el test buy_correct_quantity_of_products, no comprobamos en todos los casos que ocurriría sí:

this.pXQuantity = quantity

Vamos a añadir los casos al test a ver que nos dice PIT:

Con los cambios realizados, hemos conseguido matar a todos los mutantes, por lo que los teses han pasado.

new pit result

Conclusiones

Las pruebas unitarías tienen una gran importancía en nuestros desarrollos, si las apoyamos con pruebas de mutación conseguimos garantizar una mayor cobertura.

Con las pruebas de mutación no solo nos curamos en salud de que nuestro código funcione correctamente, si no que comprobamos que nuestras pruebas den la cobertura que esperamos.

Se escucha poco hablar sobre el “mutation testing”, pero bajo mi opinión es un buena práctica el meterlo en nuestra estrategía de calidad, ya que es un arma más para mejorar el alcance de nuestras pruebas.

Bibliografía

  1. http://pitest.org/
  2. https://www.computer.org/csdl/mags/co/1978/04/01646911.pdf
  3. http://antares.sip.ucm.es/tarot09/index_files/MutationTestingTAROT09.pdf

API Mocking con Apiary y SoapUI

Introducción

En muchas ocasiones en las que desarrollamos aplicaciones es necesario consumir APIs externas a nuestra aplicación. Este hecho a veces implica que en nuestros entornos de desarrollo o de pruebas tengamos que usar un sandbox, apuntar al entorno de pruebas de la API o, en caso extremo, apuntar al entorno de producción de la API.

Pero vemos varios puntos en contra de su uso:

  • Dependencia de sus entornos. Cuando su entorno no esté disponible no podremos ejecutar nuestras pruebas ya que es probable que nos fallen.
  • Tiempo de ejecución de los teses. El que nuestros teses tengan que comunicarse con servicios externos hará que el tiempo de ejecución aumente. Podéis ver una charla interesante sobre la reducción de tiempo de teses.

Una alternativa es simular la respuesta que esperamos, es decir, mockear la API externa.

API mocking

Como hemos comentado anteriormente, cuando hablamos de mockear una API, estamos queriendo simular cada respuesta que nos devuelve en base a la petición que hagamos.

La ventaja de mockear la API es que no necesitamos depender de que el entorno esté disponible. Otra ventaja importante es que reduciremos el tiempo de las pruebas, obviamos cualquier lógica de la API, ya que devolvemos una respuesta directa a la petición. Si el servicio mock lo tenemos en local o en nuestra red interna, la comunicación será más rápida.

Como desventaja se encuentra el hecho de que no realizamos las pruebas contra un entorno “real”, por lo que ante cualquier cambio en la API, tendremos que actualizar nuestro mock siempre y cuando el cambio nos afecte.

Una de las tareas más importantes es la de definir bien los diferentes casos, es decir, los diferentes usos o peticiones que vamos a realizar a la API.

Vamos a ver dos herramientas (con sus ventajas y desventajas) con las que mockear una API.

APIARY

URL: https://apiary.io/

Apiary es una herramienta online, que mediante un editor sencillo nos permite diseñar la respuesta que queremos devolver a una petición concreta.

apiary editor

Veamos el ejemplo default de una petición GET:

Cuando hagamos una petición GET al path /questions nos devolverá una respuesta 200 con listado de preguntas en formato json.


Apiary nos proporciona un inspector de las peticiones que se realizan al mock server, de tal forma que podemos tracear tanto las peticiones correctas como las incorrectas.

apiary inspector
apiary inspector

Ventajas

Apiary nos aporta una interfaz muy sencilla para editar nuestra API mediante API Blueprint. Otro de los puntos favorables es que mediante el editor generaremos la documentación de nuestro Mock API.

El disponer de un inspector nos ayudará a trazear las diferentes peticiones que se realizan a nuestro mock. En mi caso esto ha sido muy interesante, ya que en un proyecto pude ver peticiones que no tenía contempladas para mockear y testear.

Es un servicio SaaS, por lo que no necesitamos preocuparnos de alojarlo y gestionarlo en nuestros entornos.

Desventajas

La mayor desventaja de Apiary es que es muy simple, es decir, no nos permite meter lógica más allá de lo que es devolver siempre la misma información.

Por ejemplo: Si realizo la petición GET /questions, siempre vamos a recibir el mismo body.

Y si por ejemplo nos interesa disponer de cierta variación de datos como un body aleatorio o generado de datos de la base de datos, no vamos a tener la robustez que buscamos. No obstante, es una muy buena herramienta si no queremos mockear a ese nivel.

SOAPUI

URL: https://www.soapui.org/

Curiosamente yo había usado SoapUI para testear servicios web SOAP, pero no sabía que SoapUI permitiera mockear una API y menos para API REST.  Lo permite y encima en la versión OpenSource.

Para hacer la prueba vamos a mockear el API Mock que hemos publicado en Apiary, así nos ahorraremos el crear nosotros un API. Obtenemos las preguntas mediante una llamada GET al path /questions

soap ui request

Cuando generamos una nueva petición, nos permite crear el REST Mock Service. Lo que me gustaría detallar es que en el caso de SoapUI sí se nos permite generar respuestas dinámicas.

Veamos un ejemplo, donde la primera key de votes tiene el valor ${votes1}

Las respuestas dinámicas las podemos crear mediante el desarrollo de scripts a nivel de petición o de respuesta, ya que SoapUI nos permite definir más de una respuesta por petición.

Vamos a hacer un ejemplo de un script que va a obtener un dato de la base de datos para setearlo en la variable ${votes1} de la respuesta definida.

soapui_script

Si hacemos la petición al servidor, vamos a ver como nos va a setear el resultado de la base de datos.

El lenguaje de scripting es “Groovy script” por lo que si has desarrollado en Groovy o en Java no te va a ser difícil.

Ventajas

SoapUI nos va a permitir desarrollar lógica en nuestros mocks por lo que si necesitamos mayor dinamismo en las respuestas nos va a ser de gran ayuda.

Desventajas

Como desventaja frente a Apiary, es necesario instalar SoapUI en el servidor para poder ejecutar el servidor mock (se puede hacer desde línea de comandos). Y no disponemos de un “inspector”, lo que si es posible es guardar la información del log de SoapUI.

Conclusiones

Si lo que necesitamos es mockear una API sin necesidad de disponer diferentes respuestas, Apiary nos puede ser de gran ayuda. Sin embargo, en el caso de querer “ir más allá” necesitaremos añadir cierta lógica: SoapUI es una opción ideal.

No obstante, recordemos que nuestra intención es mockear, no crear otro servicio web.

 

Testing web en dispositivos móviles mediante Appium

Introducción

Hoy en día muchos “sites” reciben más visitas por parte de dispositivos móviles. Esto ha hecho que las empresas quieran invertir cada vez más esfuerzos en “testear” sus aplicaciones web en sistemas operativos móviles (android, ios, windows etc.).

Podemos testear nuestras aplicaciones móviles en dispositivos reales o virtuales, cada una de las opciones con sus ventajas y desventajas:

Testing en dispositivos reales

  • Su mayor ventaja es que las pruebas van a ser reales y objetivas ya que estamos probando contra el dispositivo real.
  • Su mayor desventaja es que es necesario de grandes recursos económicos, dado que necesitamos tener más de un dispositivo con diferentes versiones de SO para garantizar una buena cobertura. Normalmente se hace un estudio previo de los dispositivos y SO más utilizados o los que más acceden a nuestra aplicación. Por no decir que gestionar una “granja” de dispositivos móviles no es fácil y es caro.

Testing en dispositivos virtuales

  • Su mayor ventaja se basa en la capacidad de crear diferentes imágenes de dispositivos y SO de una forma rápida y barata.
  • Su desventaja es que las pruebas no son del todo reales y objetivas debido a que no lo lanzamos contra un dispositivo real.

Aún así en mi caso prefiero realizar pruebas sobre dispositivos reales, y es lo que haremos en este post.

Amazon AWS

Pero antes de meternos a diseñar la arquitectura de nuestra “mini granja” y desarollar las pruebas, es necesario comentar que si vuestra empresa tiene recursos económicos suficientes, Amazon dispone de una granja de dispositivos móviles en los que podréis hacer pruebas.

https://aws.amazon.com/blogs/aws/aws-device-farm-test-mobile-apps-on-real-devices/

Arquitectura del entorno de pruebas

Antes de empezar a desarrollar el proyecto de pruebas tenemos que pensar en como queremos que sea nuestro entorno de pruebas. Para eso debemos tener en cuenta nuestras necesidades.

La arquitectura de ejemplo que planteo se basa en los siguientes componentes:

Appium arquitectura

  • Servidor Appium para SO Android: Servidor en el que se encontraran conectados los dispositivos android en los que se lanzaran las pruebas. Más información sobre Appium.
  • Servidor Appium para SO iOS: Servidor en el que se encontrarán conectados los dispositivos iOS en los que se lanzaran las pruebas. Para lanzar las pruebas en SO iOS son necesarias las librerias OS X por lo que necesitaremos un entorno MacOS.
  • Repositorio Git: Repositorio donde se encuentran los proyectos de testing. En mi caso uso GitHub.
  • Jenkins: Jenkins se encargará de lanzar las tareas de testing. Descargará los proyectos del repositorio Git, los ejecutará y obtendrá los resultados.  Más información sobre Jenkins.

La arquitectura planteada nos permitirá de forma sencilla escalar el número de dispositivos. Si no queremos montar una aplicación para gestionar la ejecución de las pruebas, Jenkins puede sernos de gran ayuda, ya que mediante sus plugin nos permitirá ejecutar los proyectos y obtener los resultados.

Levantando los servidores Appium

En este post no vamos a hablar de como montar la arquitectura planteada, pero si vamos a tratar de levantar un servidor de appium para dispositivos android, en el que poder ejecutar nuestras pruebas.

Lo cierto es que la instalación de appium es muy sencilla, en mi caso haré la instalación en un Ubuntu:
Distributor ID: Ubuntu
Description: Ubuntu 16.04.1 LTS
Release: 16.04
Codename: xenial

  1. Debemos tener instalado node ya podemos isntalar appium:
    # npm install -g appium
    # npm install wd
  2. Para levantar el servidor de appium únicamente hay que ejecutar el comando: appium
    Ojo, la última versión de appium contiene una versión de chromedriver incompatible con la última versión de chrome por lo que es necesario descargar a nuestro servidor la última versión de chromedriver y ejecutar appium con ese driver, mediante:
    # appium --chromedriver-executable [PATH CHROMEDRIVER]

Ya tendremos listo nuestro servidor de appium y a la escucha de peticiones.

Preparar y conectar el entorno Android

 

  1. Para poder lanzar las pruebas y conectar el dispositivo es necesario que tengamos instalado Android SDK en el servidor. En mi caso he descargado únicamente las librerías SDK, pero podéis acceder a la página oficial y descargaros android studio. En cualquier caso en internet hay una cantidad importante de blogs de como instalarlo.
  2. Una vez que lo tengamos instalado podemos conectar nuestros dispositivos físicos al servidor. Únicamente hay que conectarlos vía USB y ejecutar el siguiente comando, que levantará el servicio:
    # adb devices
    List of devices attached
    * daemon not running. starting it now on port 5037 *
    * daemon started successfully *
    XXXXXXXXXX device

Construyendo nuestro proyecto de pruebas

Mediante los pasos anteriores ya hemos levantado el servidor appium y conectado los dispositivos sobre los que lanzaremos las pruebas. El siguiente paso es crear el proyecto de pruebas.

github

 

En el siguiente enlace podéis descargar el proyecto de pruebas de GitHub. Es importante recalcar que el proyecto está desarrollado en Groovy haciendo uso del framework de testing Spock.

 

Vamos a comentar las partes más importantes del proyecto:

Configuración de entornos en environment.groovy

En el fichero environment.groovy vamos a definir los datos de los diferentes dispositivos que van a formar parte de nuestra granja de dispositivos:

  • Device: El nombre del dispositivo. Cuando hemos lanzado el comando “adb devices” nos ha listado los diferentes dispositivos conectados.
  • Platform: La plataforma en la que se lanzan las pruebas. En el siguiente enlace podéis ver todas las plataformas permitidas.
  • Browser: Navegador sobre el que se van a lanzar las pruebas. En el siguiente enlace podéis ver el listado de navegadores permitidos.
  • Server: URL del servidor appium. En este caso son servidores locales, pero si fueran remotos sería la URL remota. He añadido el parámetro dependiente de cada dispositivo debido a que cada dispositivo puede estar en un servidor diferente, no necesariamente en el mismo. Vamos a tomar los dispositivos como entornos independientes.

Configuración de entornos en Build.gradle

Una de las características más importantes que hemos definido en nuestra arquitectura es la disposición de diferentes dispositivos sobre lo que lanzar las pruebas. Para ello tenemos que dotar al proyecto de la capacidad de ejecutar las pruebas en cualquier entorno.

Para ello en el fichero build.gradle vamos a añadir las siguientes lineas:

  1. Mediante el método loadConfiguration obtendremos los datos del dispositivo donde ejecutaremos la prueba del fichero, que se encuentran definidos en el fichero environment.groovy.
  2. Una vez que se hayan obtenido los datos, estos se reemplazarán en el fichero test/groovy/resources/device.properties mediante la tarea processTestResources, que reemplazará los valores de los tokens definidos entre dos @.
    Podéis seguir el siguiente enlace para más información de como hacer uso de replaceTokens.

Clases de prueba (Specs)

En la fase de configuración del test (setup) vamos a obtener las propiedades del fichero device.properties para crear el objeto capabilities, donde se definen los datos del dispositivo en que ejecutaremos las pruebas. WebDriver será el objeto encargado de mandar los comandos o las ordenes al servidor de appium.

Eliminaremos la instancia del driver cuando se finalice la prueba en el paso cleanup().

Por lo que al test respecta, en este caso un test muy sencillo, únicamente hay que hacer uso del driver de selenium.

 

Ejecutando las pruebas

Para ejecutar las pruebas vamos a tener que cambiar un poco el flujo de trabajo de las tareas de gradle. Normalmente cuando ejecutamos un test de un proyecto gradle lo ejecutamos de la siguiente manera:

# gradle test

Ahora vamos a tener que ejecutar otras tareas con anterioridad para que genere el build del proyecto con los datos del entorno sobre el que queremos ejecutar las pruebas:

# gradle clean build -Penv=android1 test

  1. clean: Limpiaremos el directorio build para que genere uno nuevo.
  2. build -Penv=android1:  Environment definido en el fichero environment.groovy. De donde cogerá los datos para generar el build.
  3. test: Lanzar los teses del proyecto.

Conclusiones

Cada vez es mayor la gente que accede a nuestras aplicaciones webs mediante dispositivos móviles, por lo que cada vez tiene que ser mayor la tendencia de implementar pruebas móviles.

Si bien es cierto que lanzar pruebas sobre dispositivos reales es costoso, creo que es necesario invertir dinero y esfuerzos dado que las pruebas sobre dispositivos virtuales no nos van a dar la misma seguridad al no ser del todo reales.

Como hemos visto en el post, podemos montar una infraestructura de pruebas sencilla con varios dispositivos. Aún así si lo que queremos es disponer de una granja de dispositivos y despreocuparnos de la gestión de la misma, siempre podemos mirar algún servicio como el que ofrece Amazon AWS.

Referencias

  1. http://appium.io/
  2. https://docs.gradle.org/current/userguide/working_with_files.html#filterOnCopy
  3. https://chrislormor.wordpress.com/2013/10/27/customizing-gradle-builds-for-different-environments/

Testing de microservicios – Estrategias

Introducción

Cada vez va cogiendo más fuerza el desarrollo usando arquitectura de microservicios por lo que el mundo de la calidad de software ha necesitado de nuevas estrategias. En este post hablaremos diferentes estrategias que se utilizan para garantizar la calidad de los microservicios.

Breve introducción a los microservicios

La arquitectura en microservicios busca desarrollar una aplicación dividiéndola en pequeños servicios (cada uno con su lógica de negocio) que corren de forma autónoma y que se comunican entre sí. Pero, ¿Que beneficios e inconvenientes nos aporta frente a la arquitectura monolítica?

microservicios

Como se puede apreciar en la imagen, una arquitectura monolítica es divida en varios servicios creando así una arquitectura de microservicios, donde cada servicio se comunica entre sí aportando la funcionalidad que ofrecía en su arquitectura monolítica.

 

BENEFICIOS

  • Cada microservicio es independiente por lo que su despliegue y desarrollo también, un cambio en uno no afecta en los demás.
  • La comunicación se  realiza mediante su interfaz pública (API) por lo que el lenguaje de desarrollo de los microservicios es independiente.
  • Mayor gestión de la escalabilidad, podemos escalar solo los microservicios que reciban más carga.

INCONVENIENTES

No todo son beneficios y es que la arquitectura en microservicios también nos aporta ciertos inconvenientes y retos, muchos de ellos debido a la introducción del concepto de sistemas distribuidos.

  • En el caso que una aplicación se componga de muchos microservicios es necesaria una mayor coordinación en los despliegues.
  • Será necesario una buena monitorización debido a que nos basamos en una arquitectura con un flujo muy dependiente de la comunicación. Un error en un punto del flujo tiene que ser rápidamente reconocible.
  • Garantizar la consistencia de los datos es un punto a tener en cuenta.
  • La saturación de la red puede afectar gravemente en el rendimiento de nuestra aplicación.
  • Las estrategias de calidad son mucho más complejas que en sistemas monolíticos debido a que no solo tendremos que testear los microservicios de forma independiente si no la funcionalidad en el flujo de los datos.

Estrategias de testing 

Como hemos dicho en la introducción, el mundo de la calidad del software ha tenido que aplicar nuevas estrategias de calidad para el reto que supone garantizar la calidad de las aplicaciones con arquitectura de microservicios.

En mi opinión el reto no se encuentra en el testing individual de cada microservicio, si no, en la integración y en la garantía de la consistencia de los datos. En comparación con los sistemas monolíticos, en una arquitectura por microservicios o distribuida el QA/Tester va a necesitar de un mayor conocimiento de la arquitectura y del flujo existente, debido a que es necesario controlar y verificar la información en todos los puntos de comunicación así como la funcionalidad de todos los microservicios.

La estrategia principal o las pruebas que se realizan son las siguientes (teniendo en cuenta que la estrategia es muy dependiente del proyecto que nos encontremos):

  • Tests unitarios: Como en todo proyecto ya sea monolítico o no, son necesarias pruebas unitarias. Mediante las pruebas unitarias comprobaremos la funcionalidad de los métodos o módulos de código que creamos necesarios.
  • Tests de integración: Los teses unitarios prueban los componentes de forma aislada por lo que también necesitamos probar el comportamiento entre los métodos. Debemos tener en cuenta que solo probaremos el comportamiento de los métodos de cada aplicación de forma aislada (de cada microservicio), por lo que las llamadas entre microservicios las mockearemos.
  • Tests de contratos o de API: La arquitectura de los microservicios depende de la comunicación entre los diferentes servicios. Cada microservicio presenta una API que consumen los demás microservicios, cuando la diseñamos estamos definiendo un “contrato”. Debemos realizar los diferentes tests a las APIs de los microservicios para garantizar que se mantiene el “contrato”.
  • Tests E2E: E2E (end-to-end) o teses funcionales tratan de garantizar la calidad de nuestra aplicación sin la necesidad de mockear  ningún método o llamada. Un test recorrerá la funcionalidad entre todos los microservicios que requiera. Debemos tener en cuenta que son susceptibles a fallar por motivos agenos a la funcionalidad (problemas de red, perdida de datos etc.). En el caso de los microservicios se recomiendan seguir unas reglas que nos ayudarán a reducir el esfuerzo de nuestros teses:
    • Dado que las pruebas E2E son difíciles de mantener, debemos intentar centrarnos en probar solo las funcionalidades más importantes, probando las demás en teses unitarios o de integración.
    • El uso de WebDrivers como selenium para probar las funcionalidades de usuario tiene un coste grande en cuanto a mantenimiento por lo que la mayoría de las acciones del usuario podemos hacerlas simulando las llamadas a la API.
    • Debemos intentar mantener un entorno limpio para cada ejecución dado que las pruebas son muy dependientes de los datos y pruebas anteriores pueden desestimar el resultado de los teses.

Referencias

  1.  http://testdetective.com/microservices-testing/
  2. http://www.sczyh30.com/vertx-blueprint-microservice/index.html

Explorando el “Exploratory Testing”

Introducción

 

Después de leer en varios blogs entradas sobre “exploratory testing” y como ha evolucionado desde 1.0, 1.5, 2.0 y 3.0 me he propuesto hacer un pequeño artículo para ayudar a valorar su importancia más allá de la teoría y ver su practicidad.
No voy a entrar a hablar sobre la evolución del testing exploratorio dado que James Bach ya lo explica de forma bastante detallada en en su artículo.

Exploratory testing

 

El ET (exploratory testing) sigue siendo una parte importante en la garantía de la calidad de nuestro software, a la que en menor o mayor medida no damos tanta importancia. La mayor importancia en los test exploratorios y clave para su éxito son sus bases o sus características:

  • Aprendizaje de la aplicación
  • Diseño de tests
  • Ejecución de tests

Resultado de imagen de exploratory testingEl QA deberá garantizarlas de forma simultanea a medida que va probando la aplicación, es decir, va diseñando las pruebas y ejecutándolas al tiempo que va adquiriendo conocimientos de la aplicación.

Esto puede generar disparidad de opiniones dado que al tester se le puede exigir un conocimiento del funcionamiento de la aplicación para poder validar las diferentes funcionalidades que la componen. Pero debemos ir un paso más adelante.  No estamos hablando de basar la calidad de la aplicación en este tipo de teses, si no de hacer uso de ellos para potenciar las habilidades de los testers y la búsqueda de bugs.

Algunos podréis estar de acuerdo conmigo, en el hecho de que cuando realizamos pruebas sobre una nueva funcionalidad o una nueva aplicación nuestra atención es mayor (menos comodidad, menos monotonía, desconocimiento de la funcionalidad etc), esto suele generar mejores resultados en cuanto a encontrar bugs.

Y como hemos podido observar la herramienta más importante para este tipo de test son las skills o las capacidades del tester en cuestión. Es importante disponer de la capacidad de pensar o diseñar los casos de prueba a medida que se avanza en las pruebas, discernir lo que es importante probar durante la sesión de pruebas, localizar los criterios de aceptación, en resumen, disponer de cierta experiencia e imaginación para el testing.

No obstante la experiencia se gana a medida que se hacen pruebas, por lo que es una muy buena herramienta para que los testers ganen y mejoren sus habilidades.

Experiencia y opinión

 

En el tiempo que llevo como QA lo cierto es que tengo más experiencia en la automatización de pruebas funcionales o manuales que exploratorias por lo que he dispuesto de un test plan (ya sea heredado o diseñado por mi) para desarrollar o ejecutar las pruebas.

Ahora cumpliré casi 10 meses en la empresa actual y realizamos  tanto automatización como pruebas manuales. Durante mi proceso de aprendizaje de la aplicación enfoque una gran parte del tiempo y del esfuerzo para realizar pruebas exploratorias lo que me ha llevado a tener un amplio conocimiento de la aplicación y a encontrar bugs que se salían del circuito “básico” de de pruebas. Hoy en día a pesar de que tenga interiorizado ese conocimiento y realice pruebas basadas en un test plan (que intento actualizar) suelo buscar un hueco para realizar pruebas exploratorias y sorprendentemente siempre suelo encontrar algún bug.

Siempre hay que tener en cuenta la situación o la estrategia del aseguramiento de la calidad de la empresa, pero como opinión personal creo que es importante implementar en la estrategia de calidad unas horas por recurso para que se realicen pruebas exploratorias. Esto va a ayudar a encontrar más fallos en la aplicación y sobre todo a mejorar las habilidades del tester.

Bibliografía

  1. http://www.satisfice.com/blog/archives/1509

 

Predicción de bugs, mito o realidad!

Introducción

Desde hace tiempo me produce bastante curiosidad el hecho de que puedan existir sistemas o algoritmos que nos puedan ayudar a “predecir” la existencia de bugs en nuestro software. Así leído puede parecer idílico e incluso algo difícil de creer dado que de base ya podemos pensar que hay muchos factores que pueden afectar a la inserción de un bug en nuestro código.

Para la predicción de bugs necesitaríamos una cantidad de datos importantes respecto al código, cambios realizados en la release, histórico de bugs. Y con todo ese caldo de datos…pum… magia?

Para que veáis que no solo puede afectar la parte técnica como curiosidad os dejo este enlace a un post que sobre todo es curioso.

Así que me he puesto a investigar un poco y ver la realidad y sobre todo los beneficios que nos puede aportar.

Algoritmo de predicción de bugs – FixCache

Existen varios algoritmos en lo que a la predicción de bugs respecta pero en este artículo me gustaría centrarme en el algoritmo FixCache y realizar una comparativa en artículos posteriores.

Actualmente existen dos tipos de estrategias para la predicción de bugs:

  1. Basados en las métricas de calidad como lineas de código (LOC) y la complejidad ciclomática.
  2. Basados en el control de cambios e histórico del repositorio.
fixcache_ratevstime
Imagen 1: Hit rate vs time for projects

FixCache entraría en el segundo grupo, dado que se sirve del repositorio de código para analizar los cambios y así predecir los posibles bugs.

En la imagen 1 vemos un ejemplo de la tasa de éxito del algoritmo FixCache sobre cuatro aplicaciones diferentes.

El algoritmo trabaja observando los cambios realizados en los ficheros o los métodos y utiliza tres heurísticas para añadir los ficheros a la “caché”:

 

 

 

  • Nuevos ficheros de código o modificados los cuales pueden contener bugs.
  • Ficheros que actualmente contienen bugs son propensos a contener más bugs (Aquí entra la teoría del “defect clustering”)
  • Ficheros que han sido modificados para solucionar algún bug son propensos a contener otros bugs.

Como hemos indicado para crear la “caché” de datos se sirve del repositorio de software, donde identificará los cambios realizados en los ficheros mediante los commits  al proyecto comparándolo con su propia base de datos o caché.

La gestión del caché es importante, normalmente cacheando un 10% de los ficheros del repositorio al finalizar el periodo de análisis. Es importante determinar si la caché está completa para remover los ficheros que no vayan a formar parte de posteriores análisis, para ello se pueden implementar varias políticas de reemplazo (parecido a las de la gestión de memoria caché de nuestro sistema):

  • El fichero menos utilizado recientemente (LRU).
  • El fichero que menos cambios ha tenido .
  • El fichero que menos bugs ha tenido históricamente.
  • El fichero que menos autores de cambios tenga.

Sería interesante ver que política de reemplazo da mejores resultados, lo cierto es que la más utilizada es LRU, me imagino que por su baja complejidad de gestión respecto a los demás.

Practicidad del sistema de predicción de bugs

Ya hemos visto un ejemplo de algoritmo que se basa en los cambios en los ficheros del repositorio pero,  ¿Que utilidad podemos darle al uso de un algoritmo de predicción de bugs?

En un sistema de CI (continuous integration) donde se aplique la metodología ágil, donde se realizan cambios a diario en el código y donde la intención es realizar despliegues a producción continuamente puede ser una muy buena opción para ayudar al equipo de QA en sus pruebas. Pongamos varios ejemplos:

  • Lanzar pruebas de regresión automáticas de aquellas funcionalidades o aquellos ficheros que el algoritmo detecte con riesgo de bugs en cada RC (release candidate).
  • Crear un “semaforo” que identifique las zonas de riesgo de la RC y así focalizar las pruebas o reforzarlas para prevenir bugs.
  • Mantener un histórico de ficheros conflictivos que puede ser utilizado a modo informativo por QA, desarrolladores etc.

Conclusiones

Depende de nuestro sistema de CI y la finalidad puede que nos sea más o menos viable implantar un sistema de predicción de bugs, lo cierto es que no muchas empresas disponen de ello (que se sepa) y puede ser interesante únicamente en los casos que se realicen cambios en nuestro código continuamente.

Bibliografía

  1. https://users.soe.ucsc.edu/~ejw/dissertations/AdaptiveBugPrediction_SungKim_Thesis.pdf
  2. http://static.googleusercontent.com/media/research.google.com/es//pubs/archive/41145.pdf
  3. http://www.mysmu.edu/faculty/davidlo/papers/csmr13-reopened.pdf
  4. https://users.soe.ucsc.edu/~supertri/docs/msr2011-final.pdf
  5. http://www.softwaretestingclub.com/profiles/blogs/defect-clustering-pesticide-paradox
  6. http://www.malavida.com/es/noticias/control-biometrico-para-predecir-errores-de-codigo-en-programacion-006101

Y que me dices de “Agile Testing Quadrants” – Parte III

Introducción a “Agile Testing Quadrants”

En los anteriores post de la serie hemos dado una pequeña introducción de lo que son los cuadrantes del testing  y las pruebas que realizamos en el primer cuadrante en “Y que me dices de “Agile Testing Quadrants” – Parte I” .

La importancia del segundo cuadrante y sus pruebas las tratamos en el post Y que me dices de “Agile Testing Quadrants” – Parte II” .

En este artículo vamos a hablar sobre el tercer cuadrante de los !Agile Testing Quadrants.

Agile Testing Quadrants

Tercer cuadrante – Q3

Para el cliente no solo es importante que el software funcione, el cliente espera que la aplicación funcione como tiene que funcionar en todos sus aspectos, es decir, que se cumplan todas sus necesidades y las “features” que se ofertan.

El tercer cuadrante de los “Agile Testing Quadrants” se preocupa de probar que el producto es el deseado, que la aplicación dispone de las funcionalidades que se han definido y que estás son correctas. En ocasiones nos encontramos con que se desarrolla una nueva funcionalidad que no es lo que espera el cliente, esto puede ser debido a que no se ha entendido bien los requerimientos, no se ha realizado un buen análisis o faltan casos por contemplar.

La clave de las pruebas del tercer cuadrante es ponernos en la piel del cliente, ya que vamos a intentar garantizar que se cumplen sus necesidades y que las funcionalidades son correctas.

En este tipo de pruebas emularemos el comportamiento del usuario mediante pruebas manuales. Realizar pruebas manuales requiere de ciertas habilidades por parte del tester:

  • Organización: Antes de realizar las pruebas es necesario organizar correctamente tanto las rutas de test para garantizar una buena cobertura como disponer de los datos necesarios para realizarlo.
  • Curiosidad: Durante el transcurso de las pruebas no puede faltar la curiosidad dado que vamos a tener que probar funcionalidades que probablemente no hayamos probado anteriormente.
  • Intuición: En muchos casos se detectan fallos o errores debido a que anteriormente nos hemos peleado con un escenario similar en el que hemos encontrado algún fallo. Este conocimiento nos dará cierta intuición en las pruebas.
  • Empatía: En muchos casos tenemos que ponernos en la piel del usuario para realizar ciertas pruebas. El usuario no siempre hace uso de las funcionalidades correctamente o no las utilizan como o para lo que se hicieron.

Me gustaría centrarme un poco más en el “Exploratory testing” pilar del tercer cuadrante. Este tipo de pruebas requiere de cada una de las habilidades que hemos hablado anteriormente. Durante la sesión el tester diseñará y ejecutará las pruebas analizando los resultados. La clave de este tipo de pruebas es que nos ayudará a aprender y a obtener mayor conocimiento de la aplicación que se está probando y lo más importante, a encontrar la mayoría de los bugs.

Como en los posts anteriores os hablaré de mi experiencia. Donde trabajo actualmente las aplicaciones que tenemos tienen muchos casos de prueba, muchas funcionalidades que aportan una mayor complejidad y mayor reto a los QAs. Normalmente me dedico a probar las RCs, a desarrollar pruebas automáticas y  a comprobar los bugs que se publican, pero cuando saco un rato libre me dedico a realizar “Exploratory testing” donde encuentro la mayor parte de los bugs, cierto es que son bugs antiguos que no han entrado con las nuevas funcionalidades. El realizar este tipo de pruebas me ha hecho aprender de la aplicación y a plantear dudas que se han podido traducir en conocimiento, bugs o en casos que no se han contemplado para dicha funcionalidad.

Conclusiones

 

El tercer cuadrante de los “Agile Testing Quadrants” se centra en comprobar que se cumplen las necesidades del cliente y que las funcionalidades de la aplicación son correctas. Para ello el tester tendrá que ponerse en la piel del usuario y disponer de ciertas habilidades que apoyen y den valor a las pruebas.

Uno de los pilares del tercer cuadrante es el “Exploratory testing” donde el tester durante la sesión diseñará, probara y analizará los resultados de las pruebas que va ejecutando manualmente, siendo el conocimiento aprendido de la aplicación uno de los objetivos más importantes.

Enlaces a la serie de posts

Y que me dices de “Agile Testing Quadrants” – Parte II

Introducción a “Agile Testing Quadrants”

En el post “Y que me dices de “Agile Testing Quadrants” – Parte I”  hablamos de lo que eran los cuadrantes del “testing” y dimos una pequeña introducción a las pruebas que se realizaban en el ámbito del primer cuadrante.

En este capítulo de la serie vamos a hablar de la finalidad de las pruebas del segundo cuadrante.

 

Testing Quadrants

Segundo cuadrante – Q2

Las pruebas del segundo cuadrante dan soporte al equipo de desarrollo desde un nivel más alto. Mediante las pruebas manuales y automáticas verificaremos que se cumplen los requerimientos de negocio.

Lo importante de las pruebas del segundo cuadrante es que intentaremos automatizar la mayoría de las mismas, cierto es que posiblemente haya algunas pruebas que tengamos que hacerlas manualmente debido a la complejidad de la automatización o al interés mismo de la prueba bajo el coste de automatización.

A diferencia del primer cuadrante en el segundo se realizan mayor tipo de pruebas para verificar los requerimientos de negocio:

  • Pruebas funcionales para verificar la funcionalidad de la aplicación. El mayor ejemplo lo podemos ver en las pruebas a nivel GUI, que validan las funcionalidades de la aplicación desde la capa visual. Se desarrollan con frameworks que ayudan a automatizar las acciones en el navegador, como por ejemplo selenium.Pero las pruebas funcionales no solo se realizan a nivel GUI, también se realizan otro tipo de pruebas funcionales como por ejemplo de API.
  • Pruebas de historia, para verificar que se cumplen correctamente las historias definidas desde negocio.

Estas pruebas nos exigen tener un conocimiento amplio de las necesidades a cumplir por lo que nos exigirán tener una gran comunicación tanto con negocio como con desarrollo, así como disponer de ejemplos que nos ayuden a diseñar las pruebas.

Al igual que en post Y que me dices de “Agile Testing Quadrants” – Parte I”  voy a aportar un poco de experiencia personal.

En una empresa anterior en la que trabajé en uno de los proyectos nos encargábamos de la calidad de las aplicaciones del cliente. El cliente nos hacia llegar un documento enorme con las pruebas que hacían manualmente para que las fuéramos automatizando. Nosotros no conocíamos la aplicación, por lo que cada dos por tres teníamos que ir con el equipo funcional para preguntarles que querían automatizar y lo más importante , que querían verificar dado que la funcionalidad era tan compleja que hasta a ellos mismos a veces les costaba conocerlo. Aquí se ve la importancia de la comunicación en el proceso del diseño de las pruebas.

En la empresa que estoy actualmente es todo muy diferente, el seguir una metodología ágil lo simplifica todo. Los QA disponemos de un tablón el que vemos lo que se está desarrollando, tenemos comunicación directa con los desarrolladores y con producto por lo que las dudas las solventamos rápidamente. Esto se resume en que el diseño de las pruebas se pueden realizar antes de que la historia esté desarrollada por lo que la automatización posterior será mucho fácil.

Conclusiones

Mediante las pruebas que se realizan en el segundo cuadrante verificaremos los requerimientos de negocio de nuestra aplicación pero para poder diseñar las pruebas es muy importante la comunicación tanto con desarrollo como con negocio.

Es posible que en algunos libros o artículos se categoricen las pruebas del segundo cuadrante como pruebas de aceptación, pero en realidad las pruebas de aceptación abarcan mucho más dado que no solo compruebas los requerimientos de negocio, también los de sistema, usabilidad y rendimiento.

Enlaces a la serie de posts

Y que me dices de “Agile Testing Quadrants” – Parte I

Introducción a “Agile Testing Quadrants”

Si alguna vez habéis trabajado con metodologías de desarrollo ágil cabe la posibilidad que conozcáis u os suene el concepto de “Agile testing quadrants”. Básicamente categoriza las pruebas de “testing” que podemos realizar en base a los diferentes propósitos.

 

Agile testing quadrants

En el gráfico se divide en cuatro cuadrantes que a su vez comparten un propósito y cada cuadrante es responsable de un tipo de pruebas en concreto.

El concepto en si no pretende ser una metodología o un marco de trabajo que se tenga que seguir a rajatabla en cada fase. Únicamente es un concepto que nos ayuda a tener focalizado en qué fase o en qué contexto aplicamos un tipo de prueba u otro ayudándonos a entender su finalidad.

Explicado esto, la idea del post es aportar un poco la visión de cada punto y en algunos puntos hablaré sobre mi experiencia y mi opinión.

Primer cuadrante – Q1

Uno de los principales pilares de la calidad de la aplicación son las pruebas unitarias o de componente dado que desde fases tempranas del desarrollo, siempre que se aplique de una forma correcta, se van a estar probando las partes de código o los métodos del código más importantes que componen nuestra aplicación.

En el desarrollo ágil, la metodología TDD (test-driven development) sería el núcleo de este cuadrante debido a que primeramente se desarrolla la prueba unitaria y se va implementando el código de la lógica de la aplicación mediante la refactorización, lo que aporta mayor garantía de calidad.

En este punto creo que puedo aportar cierta experiencia. Cuando comencé a desarrollar en la primera empresa que trabajé no realizábamos pruebas unitarias y menos utilizamos metodologías ágiles, esto se debía en parte a falta de conocimiento y en parte a las dichosas fechas.

Esto suponía que un cambio en el código nos podía “romper” ciertas funcionalidad que en “teoría” funcionaban correctamente, por lo que invertíamos mayor esfuerzo en arreglar lo que rompíamos o en hacer pruebas.

Lo cierto es que el desconocimiento no me hacía ver gravedad que podía llegar a tener. Con el paso del tiempo empezamos a involucrarnos más en proyectos de QA, lo que me llevó a aprender mucho y a ver el resultado de que en un aplicación no se implementará una buena estrategia de aseguramiento de la calidad en fases tempranas (el 95% de los proyectos que vi no disponían de ningún tipo de prueba unitaria y ni lo tenían en mente).

Y ahora que trabajo en una empresa como QA y que seguimos una metodología ágil veo los beneficios de disponer de unas buenas pruebas unitarias y el uso de la metodología TDD, lo que verdaderamente nos da una mayor garantía a la hora de sacar el producto al público.

Conclusiones

Más que una conclusión, lo que me gustaría trasladar es la importancia de implementar la calidad de la aplicación desde la fase inicial de su desarrollo e incluso conceptualización.

A medida que vayamos avanzando con esta serie de posts veremos como los cuadrantes nos aportan una visión de que pruebas podemos aplicar en cada fase para garantizar la calidad de nuestra aplicación.

Enlaces a la serie de posts

Lectura recomendada

Testing Restful API response

Introducción

Hace un tiempo publiqué un artículo sobre cómo “testear” funcionalmente la Restful API, los podéis leer en:

https://qajungle.com/restful-api-functional-testing/
http://blog.engineering.ticketbis.com/rest-api-functional-testing/

En el proyecto en el que he estado trabajando he necesitado tener que validar que el json de respuesta tenga una estructura en concreto y que los campos cumplan ciertas condiciones.

Al principio no necesitaba validar grandes cosas por lo que simplemente creé una clase utilidad que me ayudará a comprobar que el json de respuesta tendría los campos que esperábamos, pero esto suponía varios problemas o deficiencias:

  • Únicamente podría validar la existencia del campo, pero no ningún tipo de restricción.
  • Duplicidad de código a medida que se añaden más pruebas.
  • Un cambio en la definición del json implicaría refactorizar las pruebas.

Debido a estos motivos pensé que tenía que haber una forma más sencilla que permitiera suplir estas deficiencias. Me acordé de cómo cuando construimos un XML definimos su esquema de tal forma que validamos su estructura de una forma sencilla, por lo que me puse a investigar y encontré lo siguiente:

http://json-schema.org/

Mediante json-schema definimos la estructura que tiene que cumplir nuestro json por lo que podía ser una buena baza ya que nos permitiría:

  • Validar la estructura y ciertas restricciones.
  • Únicamente tenemos los esquemas por lo que evitamos duplicidad de código.
  • Un cambio en la definición únicamente nos obliga a cambiar el esquema y no refactorizar el código.

Solucionamos los problemas con los que nos hemos visto y encima mantenemos cierta independencia en lo que es la validación de la estructura del json y el test funcional.

Configuración del proyecto

Lo primero que tenemos que hacer implementar un validador json que nos permita comparar nuestro esquema con la respuesta obtenida, para ello he hecho uso del plugin:

https://github.com/fge/json-schema-validator
Lo cierto es que actualmente el plugin no tiene un mantenimiento pero está desarrollado para la versión actual del esquema v4.

En nuestro caso como hemos desarrollado sobre un proyecto grails lo añadiremos en el BuildConfing.groovy como dependencia:

dependencies {
...
compile "com.github.fge:json-schema-validator:2.2.6"
}

Clase utilidad

Para poder simplificar y no duplicar código creamos una clase utilidad:

Básicamente lo que hacemos es cargar el esquema json que hemos definido y validarlo frente a la respuesta obtenida de la llamada a la API.

Una de las características es que nos proporciona un reporte de los errores de validación por lo que nos dará información de los incumplimientos.

Definición del esquema

Siguiendo el ejemplo visto en el artículo https://qajungle.com/restful-api-functional-testing/ vamos a generar el esquema para la respuesta de los post.

Únicamente definimos las tres propiedades que esperamos: id, title, body e indicamos que son campos requeridos por lo que si en la respuesta no obtenemos el campo o es nulo no pasará la validación.

Más información sobre la definición de esquemas:

http://json-schema.org/examples.html

Validando la respuesta

Para validar la respuesta únicamente llamaríamos al método de la clase utilidad pasándole la ruta del esquema correspondiente:

JsonValidationUtils.validateJson(response.text, "src/java/schemes/post.json")

Conclusiones

Si nos encontramos con el caso que tener que validar la estructura de la respuesta obtenida de Restful API puede ser interesante utilizar algún tipo de validador ya que nos aporta mayor potencia para el testeo de la respuesta así como evitar duplicidad de código y evitarnos tener que hacer gran refactorización de nuestro código.

Bibliografía

  1. http://json-schema.org/
  2. https://github.com/fge/json-schema-validator