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?

Pruebas aisladas e integradas ¿Qué son?

Introducción

Leyendo sobre estrategias de testing de microservicios en entornos PaaS, me he encontrado con un artículo sobre una charla bastante interesante que creo que tengo que compartir.

http://blog.thecodewhisperer.com/permalink/integrated-tests-are-a-scam

El post habla sobre el concepto de las pruebas integradas (que no de integración) y según J.B Raisemberg porque él cree que son una farsa. Sin duda, no podéis dejar de verlo!

En este post, analizaré dos de los conceptos que se tratan, las pruebas aisladas (isolated tests) y las pruebas integradas (integrated tests).

Qué son las pruebas integradas?

J.B Raisemberg define las pruebas integradas como:

“I use the term integrated test to mean any test whose result (pass or fail) depends on the correctness of the implementation of more than one piece of non-trivial behavior.”

tests results

Imaginemos que nuestro SUT (sistema bajo test) está compuesto por dos servicios que dependen de uno tercero. Lanzamos una suite de pruebas funcionales y como resultado obtenemos que varias de las pruebas han fallado.

 

Analizándolas vemos que son fallos debidos a la comunicación en el otro servicio.

Esto sería un ejemplo de que el resultado de nuestras pruebas funcionales dependen de otros componentes no triviales, por lo que estaríamos hablando de pruebas integradas.

En las pruebas de integración puede ocurrir lo mismo. En muchas ocasiones para poder validar una acción o una respuesta, es mayor la preparación de los componentes dependientes que la aserción del resultado. Bien es cierto, que esto puede mostrar un mal diseño de la prueba.

integrated pyramid

 

Si lo vemos bajo la pirámide tradicional del testing, las pruebas integradas serían todas las que no son unitarias, dado que tanto las de integración como las funcionales van a depender de componentes “externos” a la prueba en cuestión.

Por otro lado, las pruebas integradas aumentan considerablemente la complejidad del testing.

 

integrated paths

Supongamos que en nuestro SUT (sistema bajo test) tenemos tres componentes, con 5, 2 y 3 paths a probar. Si queremos probar toda la casuística combinatoria deberíamos tener 5*2*3 = 30 pruebas. En el caso de añadir un nuevo componente, por ejemplo con 2 paths llegaríamos a las 60 pruebas!

 

Ya no solo es que el coste de mantenimiento de la suite de pruebas sea alto, es que si uno de los componentes tiene un bug, afectará a todo el conjunto de pruebas y los falsos positivos se multiplican.

El coste de mantenimiento y la complejidad de las pruebas integradas como vemos puede llegar a ser exponencial O(n!).

complexity

Para los proyectos en los que estoy trabajando me estaba planteando hacer uso de machine learning, hacer un sistema para clasificar los errores bajo ciertos patrones, debido a que tenemos una cantidad importante de falsos positivos debido a entorno etc.

Pensando de forma objetiva, montar un proyecto de tal calibre para clasificar los fallos requiere de un gran esfuerzo, y en este caso no soluciona nada, solo enmascara los problemas: el diseño de las pruebas, el entorno y la estrategia de testing. Y todo porque disponemos de una suite compleja en la que la mayoría de las pruebas son integradas.

Y… ¿Las aisladas?

Cuando las pruebas sobre un componente no requieran de la integración con otro componente no trivial, hablaremos de pruebas aisladas.

isolated pyramid Es el caso de las pruebas unitarias, que se hacen de forma aislada para comprobar el correcto funcionamiento de nuestro código, simulando el comportamiento o la respuesta de ciertas partes u objetos del código.

 

 

 

 

lineal

El coste de mantenimiento y la complejidad de las pruebas aisladas es lineal O(n), ya el número de pruebas depende del número de paths que tengamos que probar por cada componente.

 

Si haríamos pruebas aisladas para el caso anterior, tendríamos en total 5 + 3 + 2 = 10 pruebas y no 30 como en el caso de las integradas.

Desacoplar y aislar las pruebas integradas

Las pruebas unitarias son aisladas por naturaleza (siempre que estén bien diseñadas), pero claro, en nuestro proyecto muy probablemente tengamos pruebas unitarias, de integración, funcionales etc.

¿Pero, cómo podemos convertir las pruebas integradas en aisladas?

Bueno pongámonos en caso anterior, en el que nuestro SUT está compuesto por dos servicios que se comunican entre sí y además dependen de uno tercero. Para poder probar los servicios de forma aislada debemos simular todas las respuestas esperadas.

integrated_aislated

En la imagen de abajo, vemos que hemos aislado las pruebas de los componentes mediante contract testing. De esta forma, en el lado del servicio validamos que este responde como tiene que responder a las diferentes peticiones, y que nuestro componente frente a una respuesta concreta reacciona como debería.

integrated_aislated

El ejemplo se ha mostrado con microservicios, pero en el caso de querer aislar los componentes de una aplicación monolítica se podría seguir una aproximación, al igual que para las pruebas de integración, como bien explica Raisemberg en su video.

pyramid

Finalmente, si echamos la mirada atrás a la pirámide tradicional del testing, vamos a ver que ahora el scope de las pruebas integradas se ha reducido. Si bien es cierto que podemos aislar ciertas pruebas funcionales, no podemos aislarlas todas.

 

 

 

 

 

Conclusiones

Creo que es importante pensar en un diseño de pruebas aisladas en la estrategia que sigamos en nuestros proyectos.

Puede que el diseño o la arquitectura de nuestro SUT parezca más compleja o que requiera de una mayor tecnicidad pero tenemos que valorarlo frente a las ventajas que nos ofrece el aislar nuestras pruebas:

  • Reducir el total de las pruebas.
  • Reducir los falsos positivos, mejorando así el análisis de los resultados.
  • Mejorar la mantenibilidad de la suite de pruebas.
  • Reducir la complejidad de las pruebas.
  • Reducir el tiempo de ejecución de las pruebas, debido a que las pruebas integradas dependen de comunicar con otros componentes (que a su vez pueden comunicar con otros) y por tanto de su tiempo de respuesta.

A parte de las ventajas que nos aporta el uso de contract testing, como podéis leer en un post en este mismo blog.

Consumer-driven contract testing con Spring Cloud Contract

Introducción

Como hemos comentado en entradas anteriores, la arquitectura en microservicos cada vez está ganando más fuerza, y esto supone un reto tanto para el desarrollo como para el testing. En el post vamos a hablar sobre Consumer-driven contract (CDC) testing y cómo podemos verificar que los microservicios trabajan correctamente y según lo esperado entre ellos.

Para ello, como siempre vamos a trabajar con un proyecto de ejemplo usando Spring Cloud Contract, que podréis descargar del repositorio de GitHub.

Consumer-driven contract (CDC) testing

Conociendo la arquitectura en microservicios sabemos que el mayor reto consiste en comprobar y validar la comunicación entre los distintos servicios y para ello las estrategias más comunes son ejecutar pruebas end-to-end o crear mocks de los servicios y lanzar pruebas de integración sobre ellos.

MicroservicesCon las pruebas end-to-end vamos a comprobar la comunicación entre los servicios simulando una experiencia más cercana a el entorno de producción. Primero debemos tener levantado todo el entorno (servicios, BBDD) lo que es costoso y dificulta la trazabilidad. Por otro lado, es fácil que los problemas de comunicación hagan que nuestras pruebas se ralenticen más de lo normal o que fallen. Las dependencia de tantos componentes debilitan nuestras pruebas.

Como hemos visto en un post anterior, mockear un servicio nos elimina los problemas anteriores. Por el contrario, puede que las pruebas contra el mock sean satisfactorias, pero en el entorno de producción la aplicación falle debido a un mal diseño de los mocks.

 

 

cdc testingCon Spring Cloud Contract diseñaremos los contratos de los servicios. El contracto sencillamente es la definición del comportamiento de nuestro servicio en base a una request. Con esa definición se van a generar teses a nivel de proyecto para validar los contratos, así como, se publicará el Stub en nuestro repositorio maven contra el que lanzaremos las pruebas de los clientes del servicio (como vemos en la imagen). No obstante, lo vamos a ver ahora y quedará mucho más claro.

Entre los beneficios que hemos visto, utilizar CDC testing con Spring Cloud Contract nos aporta:

  • Poder desarrollar nuestro servicio mediante metodología ATDD.
  • Una documentación actualizada y “honesta” de las APIs que conforman nuestro sistema.
  • Disponer de un mayor control de los clientes que consumen nuestros servicios. Debido a que sabremos quien realiza las pruebas contra los Stubs publicados.
  • Los contratos publicados siempre actualizados, debido que en el proceso de publicación se encuentra integrado en el proceso de “build”.

Entendiendo el proyecto de pruebas

 

project_services

El proyecto con el que vamos a trabajar consiste en un sistema de dos servicios. Inventory-service y Account-service.

Account-service expone una API que devuelve los datos de los usuarios e inventory-service expone una API que devuelve el inventario de un usuario vendedor.

Inventory-service consume account-service para obtener los datos del usuario de los items que va a devolver cuando le llamen, por lo que vamos a lanzar las pruebas sobre el Stub publicado de account-service.

Inventory-service publica su Stub para que cualquier cliente futuro pueda ejecutar las pruebas contra él. Así que vuestra imaginación puede volar y ayudaros a construir un nuevo servicio siguiendo los pasos del CDC 😛

 

Preparando el proyecto

Antes de ponernos a definir los primeros contratos vamos a ver que dependencias necesitamos en nuestro proyecto. Los ejemplos los vamos a ver del proyecto de account-service.

Dentro del build.gradle vamos a definir también en que directorio vamos a alojar los contratos, así como donde se guardarán las clases base de tests que utilizará para generar los teses de verificación de los contratos.

Definiendo los contratos

Siguiendo con account-service vamos a ver el contracto que hemos definido para devolver los datos de un usuario en base a su id, en /src/test/resources/contracts/ShouldReturnAccountById.groovy

Los contratos tienen una semantica muy clara y diferenciada. Vemos como definimos tanto la request que esperamos como la response que devolvemos para esa request.

Con este contracto estamos definiendo que para cuando llamen al endpoint: /account/[0-9]+ vamos a responder con un estado 200 y con un json que contien esos campos y datos.

Vemos que la url tiene un regex, por lo que un ejemplo de una llamada que la cumpla sería: /account/12345.

Es un contrato sencillo, y con esto tanto desarrollador (para comenzar el desarrollo) como el cliente (para consumirlo) ya sabrían cual sería el endpoint, así como los datos que tienen que mandar y los que van a recibir.

Podéis ver toda la información del DSL de los contratos en el siguiente enlace.

Si seguimos la metodología ATDD, sabemos que deberíamos hacer un desarrollo mínimo para poder lanzar los teses y que fallen e ir refactorizando hasta que los teses no fallen.

Veamos el paso de generar los contratos y publicar el Stub. Lanzando el comando generateContractTests va a autogenerar los teses de validación del contracto que hemos hablado anteriormente.


➜ account-service git:(master) ./gradlew generateContractTests
Starting a Gradle Daemon (subsequent builds will be faster)
:copyContracts
:generateContractTests

Y cuando lanzamos el comando install publicará en nuestro maven local (.m2) el Stub. Veámoslo:


➜ account-service git:(master) ./gradlew clean install
:clean
:compileJava
:compileGroovy NO-SOURCE
:processResources NO-SOURCE
:classes
:jar
:copyContracts
:generateClientStubs
:verifierStubsJar
:install

Si accedéis a .m2/repository/com/qajungle/account-service/0.0.1-SNAPSHOT podréis ver el Stub:

account-service-0.0.1-SNAPSHOT-stubs.jar

Desarrollando y ejecutando los teses

El cliente (inventory-service) consume account-service para obtener los datos del usuario del inventario. Veamos la clase de servicio y el gateway sobre el que tendremos que desarrollar el test:

/src/main/java/com/qajungle/services/InventoryService.java

/src/main/java/com/qajungle/gateway/AccountGateway.java

Como vemos AccountGateway.java es el encargado de realizar la llamada al API de account-service. Por lo que en este punto es donde tendremos que atacar contra el Stub publicado por el servicio. Veamos el test:

/src/test/java/com/qajungle/services/InventoryServiceTest.java

Lo más importante en este punto es ver que estamos atacando al Stub del servicio mediante el tag:


@AutoConfigureStubRunner(ids = "com.qajungle:account-service:+:stubs:8082", workOffline = true)

Si lanzamos el test del proyecto veremos que todo ha ido correctamente:


➜ inventory-service git:(master) ./gradlew -Dtest.single=InventoryService test
:compileJava
:compileGroovy NO-SOURCE
:processResources
:classes
:copyContracts
:generateContractTests
:compileTestJava
:compileTestGroovy
:processTestResources
:testClasses
:test

BUILD SUCCESSFUL

Cambiemos el contracto en account-service para comprobar que lanza contra el Stub. Por ejemplo cambiendo el valor de un campo.


➜ inventory-service git:(master) ✗ ./gradlew -Dtest.single=InventoryService test
:compileJava UP-TO-DATE
:compileGroovy NO-SOURCE
:processResources UP-TO-DATE
:classes UP-TO-DATE
:copyContracts
:generateContractTests
:compileTestJava UP-TO-DATE
:compileTestGroovy
:processTestResources UP-TO-DATE
:testClasses
:test


com.qajungle.services.InventoryServiceTest > should_return_seller_information FAILED
org.junit.ComparisonFailure at InventoryServiceTest.java:28

org.junit.ComparisonFailure: expected:<“[aritz]”> but was:<“[fail]”>

El resultado es que el test fallará con un ComparisonFailure en este caso.

Conclusiones

El hacer uso de contract testing nos proporciona unos beneficios que no podemos encontrar en otras estrategias para el testing de servicios. Es muy importante sacar los valores que nos aporta ya no solo en no depender del entorno, de los teses E2E, si no de poder desarrollar mediante ATDD, disponer de una documentación fidedigna y llevar un mayor control de quien consume nuestros servicios.

A día de hoy solo he probado Spring Cloud Contract, pero me consta que existen otras herramientas como Pact, que espero probar en un futuro cercano. No obstante como habéis podido comprobar Spring Cloud Contract no es complejo de utilizar.

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.

Geb best practices, el antes y el después

foto3En la Greach (conferencia sobre groovy y grails de España) pude asistir a una charla títulada “Geb best practices” dada por Marcin Erdmann. En el post me gustaría tratar varios temas de los que se habló, compararlo a como desarrollo yo los proyectos a día de hoy, aplicar las buenas prácticas y ver el resultado.

Antes de pasar a la práctica tengo que decir que los proyectos que hablo en el “antes” estaban realizados sobre Geb 0.13.0 y ahora sobre 1.1.

Pódeis descargaros el proyecto de ejemplo desde mi repositorio de github. En el proyecto podéis ver todos los ejemplos que tratamos en el post.

Uso del “at checks”

foto3En Geb trabajamos con el page object pattern. El at check” es una “closure” definida a nivel de page que nos permite comprobar que nos encontramos en la página adecuada. ¿Cómo? Pues básicamente dentro del “at check” podemos realizar comprobaciones a nivel de página.

 

 

 

Antes

En algunos de mis proyectos uso el “at check” en cada cambio de página, es decir, cada vez que navegaba a otra página con el “to page” volvía a hacer un “at page” pensando que era necesario para realizar un cambio de contexto de page. En muchos casos el “at checker” únicamente devolvía true, por lo que aunque no se encontrará en la página siempre iba a ser correcto.

Después

Uso el “at check” únicamente para comprobar que me encuentro en la página que quiero estar. No lo útilizo como “cambio de contexto” de página ya que no es necesario si hago el “to page”.

Conclusión

En mi caso, haría un uso responsable de “at check”, es decir, no es necesario comprobar todas las páginas, si vamos a realizar acciones en la página y no nos encontramos en ella, nos va a saltar que no se encuentra el componente con el que queremos interactuar. Pero por otro lado, si hacemos uso del “at” vamos a saber que falla porque no se encuentra en la página o la página no ha cargado, y no porque el componente no se encuentra.

En el ejemplo podeís ver que yo lo he usado ya que testeo la navegación entre páginas.

Navegar a páginas

foto1

Lo común es navegar entre páginas y realizar las acciones y las comprobaciones que sean necesarias en nuestras pruebas funcionales. No siempre la url de un página es estática (ej.: /author/), si no que en muchas ocasiones necesitamos navegar a una url compuesta por elementos dinámicos (ej. /author/[ID]).

 

 

Antes

Para poder generar las url dinámicamente en base a los datos de entrada de un test hoy en día hago uso del “builder pattern”.

Sin entrar al detalle de como se contrulle un builder en groovy y explicandolo de una forma sencilla, únicamente paso los datos necesarios para construir la url al builder y una vez generada la seteo a la propiedad url de la página. Posteriormente navego a la página.
Después

Marcin comentó en la charla dos formas de poder navegar a una página construyendo la url dinámicamente, veamoslas.

La primera es con el uso del método de página “convertToPath“. El método puede recibir los parámetros que sean necesarios para construir la url.

Cuando hagamos uso del “to Page” añadirá el path construido por el método a la url estática de la página.

El otro ejemplo es el uso del método de página “getPageUrl”. Un poco más complejo. En este caso es necesario instanciar la página con los parámetros que espera la url.

Debemos hacer el “to Page” pasandole la nueva instancia de la página con los parámetros necesarios de la url en el constructor.

Conclusión

El hacer uso de un builder hace que tengamos que gestionar la lógica de la construcción de la url para cada página, y si no son muy “estandares” no será una tarea fácil.

Por otro lado hacer uso de “convertToPath” y “getPageUrl” nos permitirá mantener la responsabilidad de la construcción de la url en la propia página.

“Trackear” las páginas

foto3

Cuando hablamos de “trackear” las páginas, hablamos de capturarlas en una variable y utilizarlar sus elementos. Esto nos va aportar ciertas ventajas que vamos a ver con los ejemplos.

Antes

En todos mis proyectos lo común es navegar a la página en la que quiero e interactuar con los elementos directamente, esto hace que si la funcionalidad requiere de mucha interacción llegue un momento en que me pierda y no sepa si estoy interactuando con el elemento de una página u otro. Lo cierto es que puede llegar a ser poco legible.

El caso que presento no es el mejor ya que interactuamos con pocos elementos, pero imaginaos que hay más elementos e incluso elementos de otras páginas con el mismo nombre.

Después

Una buena practica es “capturar” la página y referenciar a los elementos a través de la variable. Veamos.

Conclusión

Capturar la página nos va a ayudar con la legibilidad de nuestros teses. En pruebas pequeñas igual no lo vemos algo muy útil, pero lo cierto es que es de gran ayuda.

Módulos

foto2

El uso de módulos nos permite reutilizar la definición del contenido de las páginas a través de varías páginas. Pero nos aportan mucho más valoro ocultar estructuras de componentes o interacciones complejas de los teses.

 

 

Antes / Después

En mi caso no hacía mucho uso de los módulos debido a que pensaba que los módulos únicamente servian para reutilizar contenido de las páginas, y no he tenído muchas ocasiones de utilizarlo. Pero saber que nos aportan mucho más que la reutilización aporta mayor valor.

Veamos un ejemplo de uso los módulos.

Por supuesto en la charla se tocaron más conceptos y buenas prácticas que comentaremos más adelante.