Capítulo 4. Pruebas continuas

Este trabajo se ha traducido utilizando IA. Agradecemos tus opiniones y comentarios: translation-feedback@oreilly.com

¡Tus esfuerzos de retroalimentación rápida están en el limbo sin una retroalimentación continua!

En el capítulo anterior, hablamos de cómo añadir pruebas en las capas adecuadas de la aplicación acelera los ciclos de retroalimentación. Es imprescindible recibir esa retroalimentación rápida de forma continua y no sólo en ráfagas aleatorias para regular sin fisuras la calidad de la aplicación a lo largo del ciclo de desarrollo. Este capítulo está dedicado a la elaboración de esa práctica de pruebas continuas.

Las pruebas continuas (CT) son el proceso de validar la calidad de la aplicación utilizando métodos de prueba tanto manuales como automatizados después de cada cambio incremental, y de alertar al equipo cuando el cambio provoca una desviación de los resultados de calidad previstos. Por ejemplo, cuando una parte de la funcionalidad se desvía de las cifras previstas de rendimiento de la aplicación, el proceso de TC avisa inmediatamente al equipo mediante pruebas de rendimiento fallidas. Esto da al equipo la oportunidad de solucionar los problemas lo antes posible, cuando aún son relativamente pequeños y manejables. La falta de un bucle de retroalimentación continuo de este tipo podría hacer que los problemas pasaran desapercibidos durante un período prolongado, permitiendo que con el tiempo se propagaran en cascada a niveles más profundos del código y aumentando el esfuerzo necesario para solucionarlos.

El proceso de TC se basa en gran medida en la práctica de la integración continua (IC) para realizar pruebas automatizadas contra cada cambio. La adopción de la IC junto con la TC permite al equipo realizar entregas continuas (EC). En última instancia, el trío formado por la IC, la DC y la TC convierte al equipo en un equipo de alto rendimiento, medido por las cuatro métricas clave: tiempo de espera, frecuencia de implementación, tiempo medio de restauración yporcentaje de cambios fallidos. Estas métricas, que veremos al final de este capítulo, proporcionan información sobre la calidad de las prácticas de entrega del equipo.

Este capítulo te dotará de las habilidades necesarias para establecer un proceso de CT para tu equipo. Aprenderás sobre los procesos CI/CD/CT y las estrategias para conseguir múltiples bucles de retroalimentación en varias dimensiones de la calidad. También se incluye un ejercicio guiado para configurar un servidor CI e integrar las pruebas automatizadas.

Bloques de construcción

Como base para la destreza en las pruebas continuas, esta sección te introducirá en la terminología y el proceso general CI/CD/CT. También aprenderás los principios fundamentales y la etiqueta que deben impregnarse cuidadosamente en el equipo para que el proceso tenga éxito. Empecemos con una introducción a la CI.

Introducción a la integración continua

Martin Fowler, autor de media docena de libros, entre ellos Refactoring: Improving the Design of Existing Code (Addison Wesley) y científico jefe de Thoughtworks, describe la integración continua como "una práctica de desarrollo de software en la que los miembros de un equipo integran su trabajo con frecuencia, normalmente cada persona lo integra al menos a diario, lo que da lugar a múltiples integraciones al día". Veamos un ejemplo para ilustrar las ventajas de seguir esta práctica.

Dos compañeros de equipo, Allie y Bob, empezaron a desarrollar de forma independiente un inicio de sesión y una página de inicio. Empezaron a trabajar por la mañana, y al mediodía Allie había terminado un flujo básico de inicio de sesión y Bob una estructura básica de página de inicio. Ambos probaron sus respectivas funcionalidades en sus máquinas locales y siguieron trabajando. Al final del día, Allie había completado la funcionalidad de inicio de sesión haciendo que la aplicación aterrizara en una página de inicio vacía después de iniciar sesión con éxito, ya que la página de inicio aún no estaba disponible para ella. Del mismo modo, Bob completó la funcionalidad de la página de inicio codificando el nombre de usuario en el mensaje de bienvenida, ya que no disponía de la información de usuario del inicio de sesión.

Al día siguiente, ambos informaron de que sus funcionalidades estaban "terminadas". Pero, ¿están realmente terminadas? ¿Cuál de los dos desarrolladores es responsable de integrar las páginas? ¿Deberían crear una historia de usuario de integración distinta para cada escenario de integración en toda la aplicación? Si es así, ¿estarán preparados para el gasto de los esfuerzos de prueba duplicados que supone probar la historia de integración? ¿O deberían retrasar las pruebas hasta que la integración esté hecha? Éste es el tipo de preguntas que se abordan implícitamente con la integración continua.

Cuando se siga el CI, Allie y Bob compartirán el progreso de su trabajo a lo largo del día (después de todo, ambos tenían listo un esqueleto básico de sus funcionalidades al mediodía). Bob podrá añadir el código de integración necesario para abstraer el nombre de usuario tras el inicio de sesión (por ejemplo, a partir de un token JSON o JWT), y Allie podrá hacer que la aplicación aterrice en la página de inicio real tras un inicio de sesión satisfactorio. Entonces, ¡la aplicación será realmente utilizable y comprobable!

En este ejemplo, puede parecer un pequeño coste adicional integrar las dos páginas al día siguiente. Sin embargo, cuando el código se acumula e integra más tarde en el ciclo de desarrollo, las pruebas de integración resultan costosas y llevan mucho tiempo. Además, cuanto más se retrasen las pruebas, más probabilidades habrá de encontrar problemas enredados difíciles de solucionar, que a veces incluso justifican la reescritura de una parte importante del software. Posteriormente, esto creará un miedo generalizado a la integración entre los miembros del equipo, ¡a menudo un acompañamiento tácito del retraso de la integración!

La práctica de la integración continua trata esencialmente de reducir esos riesgos de integración y ahorrar al equipo reescrituras y parches ad hoc. No elimina por completo los defectos de integración, pero facilita encontrarlos y solucionarlos pronto, cuando apenas están brotando.

El proceso CI/CT/CD

Empecemos por examinar en detalle los procesos de integración y prueba continuas. Más adelante, veremos cómo se conectan para formar el proceso de entrega continua.

El proceso CI/CT se basa en cuatro componentes individuales:

  • El sistema de control de versiones (VCS), que contiene toda la base de código de la aplicación y sirve como repositorio central del que todos los miembros del equipo pueden extraer la última versión del código y donde pueden integrar su trabajo continuamente

  • Las pruebas funcionales y multifuncionales automatizadas que validan la aplicación

  • El servidor CI, que ejecuta automáticamente las pruebas automatizadas contra la última versión del código de la aplicación por cada cambio adicional

  • La infraestructura que aloja el servidor CI y la aplicación

El flujo de trabajo de integración y pruebas continuas comienza con el desarrollador, que, en cuanto termina una pequeña parte de la funcionalidad, introduce sus cambios en un sistema de control de versiones común (por ejemplo, Git, SVN). El VCS realiza un seguimiento de cada cambio que se le envía. A continuación, los cambios se envían a través del proceso de pruebas continuas, donde el código de la aplicación se construye completamente y se ejecutan pruebas automatizadas contra él mediante un servidor CI (por ejemplo, Jenkins, GoCD). Cuando se superan todas las pruebas, los nuevos cambios se consideran totalmente integrados. Cuando hay fallos, el propietario del código correspondiente soluciona los problemas lo antes posible. A veces, los cambios se revierten desde el VCS hasta que se resuelven los problemas. Esto se hace principalmente para evitar que otros saquen el código con problemas e integren su trabajo sobre él.

Como muestra la Figura 4-1, Allie introduce su código para la funcionalidad básica de inicio de sesión junto con las pruebas de inicio de sesión en el sistema común de control de versiones antes del mediodía, como parte del commit Cn.

Components in a continuous integration and testing process
Figura 4-1. Componentes de un proceso continuo de integración y pruebas
Nota

En el VCS Git, un commit es una instantánea de toda la base de código en un momento dado. Cuando se practica la integración continua, se recomienda guardar los pequeños cambios incrementales como commits independientes en la máquina local. Cuando la funcionalidad alcance un estado lógico, como completar una funcionalidad básica de inicio de sesión, los commits deben empujarse al repositorio VCS común. Sólo cuando se envían los cambios al VCS comienzan los procesos de CI y de prueba.

El nuevo cambio, Cn, activa una canalización independiente en el servidor CI. Cada canal se compone de varias etapas secuenciales. La primera es la etapa de construcción y pruebas, que construye la aplicación y ejecuta pruebas automatizadas contra ella. Éstas incluyen todas las pruebas a nivel micro y macro de las que se habló en el Capítulo 3, y las pruebas que hacen valer las dimensiones de calidad de la aplicación (rendimiento, seguridad, etc.), de las que hablaremos en los próximos capítulos. Una vez completada esta etapa, se indican los resultados de las pruebas a Allie. En este caso, el código de Allie se ha integrado correctamente, y ella continúa con su funcionalidad de inicio de sesión.

Más tarde ese mismo día, Bob envía el commit Cn+1 para la función de la página de inicio tras extraer los últimos cambios (Cn) del VCS común. Cn+1 es, por tanto, una instantánea de la base de código de la aplicación que incluye los nuevos cambios de Allie y Bob. Esto desencadena la fase de compilación y prueba en el proceso CI. Cuando las pruebas se ejecutan contra Cn+1, se garantiza que los nuevos cambios de Bob no han roto ninguna de las funcionalidades anteriores, incluida la última confirmación de Allie, ya que ella también ha añadido las pruebas de inicio de sesión. Afortunadamente, Bob no lo ha hecho. Sin embargo, vemos en la Figura 4-1 que los cambios de Allie en los commits Cn+2 y Cn+3 han roto la integración, y las pruebas han fallado. Tiene que corregirlos antes de continuar con su trabajo, ya que ha introducido un error en el VCS común. Puede enviar su corrección como otra confirmación y el proceso continuará.

Imagina el mismo flujo de trabajo en un gran equipo distribuido, y comprenderás lo mucho que facilita la IC que todos los miembros del equipo compartan sus progresos e integren su trabajo sin problemas. Además, en las aplicaciones a gran escala, suele haber varios componentes interdependientes que justifican pruebas de integración exhaustivas, ¡y el proceso de pruebas continuas proporciona la confianza tan necesaria en la delicadeza de su integración!

Con ese tipo de confianza obtenida de los procesos de integración y prueba totalmente automatizados, el equipo se sitúa en un lugar privilegiado para empujar su código a producción siempre que el negocio lo exija. En otras palabras, el equipo está equipado para realizar entregas continuas.

La entrega continua depende de que se sigan los procesos de integración y pruebas continuas de para que la aplicación esté lista para producción en todo momento. Además, exige disponer de un mecanismo de implementación automatizado que pueda activarse con un solo clic para desplegarse en cualquier entorno, ya sea de control de calidad o de producción. La Figura 4-2 muestra el proceso de entrega continua.

Continuous delivery process with CI, CT, and deployment pipelines
Figura 4-2. Proceso de entrega continua con CI, CT y conductos de implementación

Como puedes ver, el proceso de entrega continua engloba los procesos CI/CT junto con las canalizaciones de implementación de autoservicio. Estos conductos también son etapas configuradas en el servidor de CI; realizan la tarea de desplegar la versión "elegida" de los artefactos de la aplicación en el entorno requerido.

El servidor CI enumera todos los commits de con el estado de los resultados de sus pruebas. Sólo si se han superado todas las pruebas de una confirmación (o de un conjunto de confirmaciones), ofrece la opción de implementar esa versión concreta de la aplicación (V). Por ejemplo, supongamos que el equipo de Allie quiere recibir comentarios de la empresa sobre la funcionalidad básica de inicio de sesión introducida como parte de la confirmación Cn. Pueden pulsar el botón Implementación Vx, como se ve en la Figura 4-2, y elegir el entorno de pruebas de aceptación del usuario (UAT). Esto desplegará sólo los cambios realizados hasta ese momento en el entorno UAT, es decir, no se desplegarán las confirmaciones Cn+1 y posteriores de Bob. Como puedes ver, los commits Cn+2 y Cn+3 no están disponibles para su implementación, ya que las pruebas han fallado.

Este tipo de configuración de entrega continua resuelve muchos problemas críticos, pero una de las ventajas más importantes que proporciona es la capacidad de lanzar al mercado las características del producto en el momento adecuado. A menudo, los retrasos en los lanzamientos de características provocan pérdidas de ingresos y de clientes a favor de la competencia. Además, desde el punto de vista del equipo, el proceso de implementación se automatiza totalmente, reduciendo la dependencia de que determinadas personas hagan su magia el día de la implementación; cualquiera es libre de hacer una implementación sin problemas en cualquier entorno y en cualquier momento. La automatización de las Implementaciones también reduce el riesgo de bibliotecas incompatibles, configuraciones ausentes o incorrectas y documentación insuficiente.

Principios y etiqueta

Ahora que hemos hablado de los procesos CI/CD/CT, es importante señalar que estos procesos sólo pueden llegar a buen puerto si todos los miembros del equipo siguen un conjunto de principios bien definidos y una etiqueta . Al fin y al cabo, se trata de una forma automatizada de colaborar en su trabajo, ya sean pruebas automatizadas, código de aplicación o configuraciones de infraestructura. El equipo debe establecer estos principios al principio de su ciclo de entrega y seguir reforzándolos a lo largo del mismo. He aquí un conjunto mínimo de principios y etiqueta que un equipo deberá respetar para tener éxito:

Haz commits de código frecuentes

Los miembros del equipo deben hacer frecuentes commits de código en y enviarlos al VCS tan pronto como terminen cada pequeña pieza de funcionalidad, para que se pruebe y esté disponible para que otros construyan sobre ella.

Confirma siempre el código autocomprobado

Cada vez que se confirma un nuevo fragmento de código de , debe ir acompañado de pruebas automatizadas en la misma confirmación. Martin Fowler denomina a esta práctica autocomprobación del código. Por ejemplo, como hemos visto antes, Allie confirmó su funcionalidad de inicio de sesión junto con las pruebas de inicio de sesión. Esto garantizó que su confirmación no se rompiera cuando Bob confirmara su código a continuación.

Adherirse a la Prueba de Certificación de Integración Continua

Cada miembro del equipo debe asegurarse de que su commit supera el proceso de pruebas continuas antes de pasar al siguiente conjunto de tareas. Si las pruebas fallan, deben repararlas inmediatamente. Según la Prueba de Certificación de Integración Continua de Martin Fowler, una etapa de compilación y prueba rota debe repararse en 10 minutos. Si esto no es posible, debe revertirse el commit roto, dejando el código estable (o verde).

No ignores/comentes las pruebas que fallan

Con las prisas por hacer pasar la fase de compilación y la fase de pruebas, los miembros del equipo no deben comentar e ignorar las pruebas que fallan. Por evidentes que sean las razones por las que esto no debe hacerse, es una práctica habitual.

No empujes a una construcción rota

El equipo no debe empujar su código cuando la fase de construcción y pruebas esté rota (o en rojo). Empujar trabajo sobre una base de código ya rota hará que las pruebas vuelvan a fallar. Esto sobrecargará aún más al equipo con la tarea adicional de encontrar qué cambios rompieron originalmente la compilación.

Asume la responsabilidad de todos los fracasos

Cuando las pruebas fallan en un área de código en la que alguien no ha trabajado, pero falla debido a sus cambios, la responsabilidad de arreglar la construcción sigue recayendo sobre ellos. Si es necesario, pueden emparejarse con alguien que tenga los conocimientos necesarios para arreglarlo, pero en última instancia arreglarlo antes de pasar a su siguiente tarea es un requisito fundamental. Esta práctica es esencial porque, a menudo, la responsabilidad de arreglar las pruebas fallidas se reparte entre unos y otros, lo que provoca un retraso en la resolución de los problemas. A veces las pruebas dejan de ejecutarse en el CI durante días mientras no se soluciona el problema. Esto hace que el proceso de pruebas continuas proporcione información incompleta o falsa sobre los cambios introducidos durante esa ventana abierta.

Muchos equipos también adoptan prácticas más estrictas en su propio beneficio, como exigir que todas las pruebas de nivel micro y macro se superen en las máquinas locales antes de enviar la confirmación al VCS, suspender la fase de creación y prueba si una confirmación no alcanza el umbral de cobertura del código, publicar el estado de la confirmación (aprobado o suspenso) con el nombre de la persona que la ha realizado para todo el mundo en un canal de comunicación como Slack, poner música a todo volumen en el área del equipo cada vez que se rompe una creación desde un monitor de CI dedicado, etcétera. Además, como probador del equipo, vigilo el estado de las pruebas en el CI y me ocupo de que se corrijan a tiempo. Fundamentalmente, todas estas medidas se toman para racionalizar las prácticas del equipo en torno a los procesos de CI/CT y obtener así los beneficios adecuados, aunque la medida principal que siempre parece funcionar mejor es dotar al equipo de conocimientos sobre no sólo el "cómo", sino también el "por qué" del proceso.

Estrategia de pruebas continuas

Ahora que conoces los procesos y principios, el siguiente paso es crear y aplicar estrategias personalizadas a las necesidades de tu proyecto.

En la sección anterior, se demostró el proceso de pruebas continuas de con una única etapa de compilación y pruebas que ejecuta todas las pruebas y proporciona retroalimentación en un único bucle. También puedes acelerar el ciclo de retroalimentación con dos bucles de retroalimentación independientes: uno que ejecute las pruebas contra el código estático de la aplicación (por ejemplo, todas las pruebas de micronivel), y otro que ejecute las pruebas de macronivel contra la aplicación implementada. Esto, en cierto modo, es un ligero desplazamiento a la izquierda en el que aprovechamos la capacidad de las pruebas de micronivel (unidad, integración, contrato) para ejecutarse más rápidamente que las pruebas de macronivel (API, interfaz de usuario, extremo a extremo) para obtener una retroalimentación más rápida.

La Figura 4-3 muestra un proceso de TC con dos etapas. Como puedes ver aquí, una práctica habitual es combinar la compilación de la aplicación con las pruebas de micronivel como una única etapa en CI. Esto se denomina tradicionalmente etapa de compilación y pruebas. Cuando el equipo se adhiere a la pirámide de pruebas, como comentamos en el Capítulo 3, las pruebas de micronivel acabarán validando una amplia gama de funcionalidades de la aplicación. Como resultado, esta etapa les ayuda a obtener rápidamente una amplia retroalimentación sobre el commit. La etapa de construcción y pruebas debe ser lo suficientemente rápida como para terminar su ejecución en pocos minutos, de modo que, según los principios y la etiqueta recomendados, el equipo espere a que finalice antes de pasar a la siguiente tarea. Si tarda más, el equipo debe encontrar formas de mejorarla, por ejemplo, paralelizando la fase de construcción y prueba para cada componente en lugar de tener una única fase para toda la base de código.1

The continuous testing process with two feedback loops
Figura 4-3. El proceso de prueba continua con dos bucles de retroalimentación
Nota

En su libro Entrega Continua (Addison-Wesley Professional), Jez Humble y David Farley sugieren que la fase de construcción y prueba debe ser lo suficientemente corta como para que ocupe "más o menos el tiempo de que puedes dedicar a prepararte una taza de té, una charla rápida, consultar tu correo electrónico o estirar los músculos".

En cuanto se supera la etapa de construcción y prueba , la etapa de implementación envía los artefactos de la aplicación a un entorno CI (a veces llamado entorno de desarrollo). La siguiente etapa, denominada etapa de pruebas funcionales o etapa de pruebas de aceptación, ejecuta las pruebas de macronivel contra la aplicación desplegada en el entorno CI. Sólo cuando se supera esta etapa, la aplicación está lista para la implementación de autoservicio en otros entornos de nivel superior, como QA, UAT y producción.

La retroalimentación de esta etapa puede llevar más tiempo, ya que las pruebas de aceptación tardan más en ejecutarse, y la etapa se activa después de la implementación de la aplicación, lo que también lleva tiempo. Pero cuando los equipos aplican correctamente la pirámide de pruebas, los dos bucles de retroalimentación deberían tardar menos de una hora en completarse. El ejemplo que di en el Capítulo 3 lo corrobora: cuando el equipo tenía ~200 pruebas de macronivel, tardaban 8 horas en obtener retroalimentación, pero cuando reimplementaron su estructura de pruebas para ajustarse a la pirámide de pruebas, tardaron sólo unos 35 minutos desde el commit hasta estar listos para la implementación de autoservicio con ~470 pruebas de micronivel y macronivel.

Otra consideración es que, cuando el bucle de retroalimentación es corto, los miembros del equipo pueden seguir dando prioridad a la solución de los problemas detectados en el proceso de pruebas continuas, aunque hayan retomado una nueva tarea poco después de la fase de construcción y pruebas. Si tardan varias horas, pueden tener la tentación de ignorar las pruebas que fallan y seguirlas como tarjetas de defectos para arreglarlas más tarde. Esto es perjudicial, ya que significa que están integrando su nuevo código sobre los defectos, y el nuevo código tampoco se prueba a fondo al ignorarse las pruebas que fallan. Por tanto, el equipo debe seguir monitorizando y adoptando formas de acelerar los dos bucles de retroalimentación mediante técnicas como paralelizar la ejecución de las pruebas, implementar la pirámide de pruebas, eliminar las pruebas duplicadas y refactorizar las pruebas para eliminar las esperas y abstraer las funcionalidades comunes.2

Este proceso de pruebas continuas puede ampliarse aún más para recibir retroalimentación interfuncional, como se muestra en la Figura 4-4. Los equipos pueden ejecutar pruebas automatizadas de rendimiento, seguridad y accesibilidad como parte de los dos bucles de retroalimentación existentes o configurar etapas separadas posteriores a la etapa de pruebas de aceptación en el servidor CI, logrando el objetivo de recibir retroalimentación rápida y continua sobre la calidad de la aplicación de forma holística. En los próximos capítulos aprenderás estrategias de cambio para las pruebas interfuncionales.

The continuous testing process with three feedback loops
Figura 4-4. El proceso de prueba continua con tres bucles de retroalimentación

Llegados a este punto, si ejecutas todas las pruebas de forma encadenada, puede que necesites mucho tiempo y recursos para terminar todas las etapas. Una forma de planificar estratégicamente el proceso de TC en este caso es dividir las pruebas en pruebas de humo y pruebas de regresión nocturnas, como se ve en la Figura 4-5.

The continuous testing process with four feedback loops
Figura 4-5. El proceso de prueba continua con cuatro bucles de retroalimentación

La prueba de humo es un término tomado de el mundo de la ingeniería eléctrica, en el que se hace pasar electricidad una vez completado el circuito para evaluar el flujo de extremo a extremo. Cuando haya problemas en el circuito, habrá humo (de ahí el nombre). Del mismo modo, puedes elegir las pruebas que cubren el flujo de extremo a extremo de cada función de la aplicación para formar el paquete de pruebas de humo y ejecutarlas sólo como parte de la fase de pruebas de aceptación. De este modo, puedes obtener rápidamente una señal de alto nivel sobre el estado de cada commit. Como se ve en la Figura 4-5, la confirmación está lista para la implementación de autoservicio tras la fase de pruebas de humo.

Cuando decides realizar pruebas de humo, tienes que complementarlas con la regresión nocturna. La etapa de regresión nocturna se configura en el servidor de CI para que ejecute todo el conjunto de pruebas una vez al día, cuando el equipo no trabaja (por ejemplo, puede programarse para que se ejecute todos los días a las 7 de la tarde). Las pruebas se ejecutan con el último código base con todos los commits del día. El equipo debe acostumbrarse a analizar los resultados de la regresión nocturna a primera hora del día siguiente y priorizar la corrección de defectos y fallos del entorno. A veces esto puede requerir cambios en los scripts de prueba, y eso también debe priorizarse para el día, de modo que el proceso de pruebas continuas proporcione la información adecuada para los próximos commits.

Puedes aplicar estas dos estrategias para dividir las pruebas funcionales y multifuncionales. Por ejemplo, puedes optar por ejecutar la prueba de carga de rendimiento para un único punto final crítico como parte de cada commit y ejecutar el resto de pruebas de rendimiento como parte de la regresión nocturna (las pruebas de rendimiento se tratan en el Capítulo 8). Del mismo modo, puedes ejecutar las pruebas de exploración de seguridad del código estático como parte de la etapa de compilación y prueba, y ejecutar las pruebas de exploración de seguridad funcional (tratadas en el Capítulo 7) como parte de la etapa de regresión nocturna. Aunque resulte obvio, la advertencia de este enfoque es que la retroalimentación se retrasa un día. En consecuencia, también se retrasa la corrección de la respuesta; los problemas se rastrean como defectos y se corrigen más tarde. En consecuencia, debes tener cuidado al elegir los tipos de pruebas que ejecutas como parte de las etapas de prueba de humo y regresión nocturna. Además, ten en cuenta que sólo las pruebas de nivel macro y multifuncionales deben clasificarse como pruebas de humo; todas las pruebas de nivel micro deben seguir ejecutándose como parte de la fase de construcción y prueba.

La mayoría de las veces, cuando la aplicación es joven, puedes renunciar a estas estrategias y disfrutar del privilegio de ejecutar todas las pruebas para cada commit. Luego, cuando la aplicación empiece a crecer (junto con el número de pruebas), puedes aplicar los distintos métodos de optimización de tiempo de ejecución de CI, y finalmente seguir el camino de las pruebas de humo y la regresión nocturna.

Beneficios

Si te estás preguntando si todo ese esfuerzo por emprender un proceso de pruebas continuas dará frutos que merezcan la pena, la Figura 4-6 muestra algunas ventajas para que tú y tu equipo os motivéis.

Benefits of the continuous testing process
Figura 4-6. Ventajas del proceso de prueba continua

Veamos cada una de ellas por separado:

Objetivos comunes de calidad

Seguir el proceso de pruebas continuas garantiza que todos los miembros del equipo sean conscientes de un objetivo de calidad común y trabajen para alcanzarlo -en términos de aspectos de calidad tanto funcionales como interfuncionales-, ya que su trabajo se evalúa continuamente en relación con ese objetivo. Es una forma concreta de incorporar la calidad.

Detección precoz de defectos

Todos los miembros del equipo reciben comentarios inmediatos de sobre sus commits, tanto en lo que se refiere a los aspectos funcionales como a los interfuncionales. Esto les da la oportunidad de solucionar los problemas mientras disponen del contexto pertinente, en lugar de volver al código unos días o semanas más tarde.

Listo para entregar

Como el código se prueba continuamente en , la aplicación siempre está lista para su implementación en cualquier entorno.

Mayor colaboración

Es más fácil colaborar con miembros del equipo distribuidos en que comparten su trabajo y hacer un seguimiento de qué commit causó qué problemas, evitando así acusaciones y limitando la animosidad.

Propiedad de entrega combinada

La responsabilidad de la entrega se distribuye en entre todos los miembros del equipo, en lugar de sólo entre el equipo de pruebas o los desarrolladores principales, ya que todos son responsables de garantizar que sus commits están listos para la implementación.

Si llevas un tiempo trabajando en la industria del software , ¡seguro que sabes lo difícil que es conseguir algunos de estos beneficios de otra forma!

Ejercicio

Es hora de ponerse manos a la obra. El ejercicio guiado te mostrará cómo transferir las pruebas automatizadas que creaste en el Capítulo 3 a un VCS, configurar un servidor CI e integrar las pruebas automatizadas con el servidor CI de forma que, cada vez que envíes una confirmación al VCS, se ejecuten las pruebas automatizadas. Aprenderás a utilizar Git y Jenkins como parte de este ejercicio.

Git

Desarrollado originalmente en 2005 por Linus Torvalds, creador del núcleo del sistema operativo Linux, Git es el sistema de control de versiones de código abierto más utilizado . Según la encuesta de Stack Overflow de 2021, el 90% de los encuestados utilizan Git. Es un sistema de control de versiones distribuido, lo que significa que cada miembro del equipo obtiene una copia de todo el código base junto con el historial de cambios. Esto da a los equipos mucha flexibilidad en términos de depuración y trabajo independiente.

Configurar

Para empezar, necesitarás un lugar donde alojar tu base de código en . GitHub y Bitbucket son empresas que proporcionan ofertas basadas en la nube para alojar repositorios Git (un repositorio, en términos sencillos, es una ubicación de almacenamiento para tu base de código). GitHub permite alojar repositorios públicos de forma gratuita, lo que lo hace popular, especialmente entre la comunidad de código abierto. Así que para este ejercicio, si aún no tienes una cuenta en GitHub, crea una ahora.

En tu cuenta de GitHub, navega hasta Tus repositorios → Nuevo para crear un nuevo repositorio para tus pruebas automatizadas de Selenium. Dale un nombre al repositorio, por ejemplo PruebasFuncionales, y conviértelo en un repositorio público. Si lo creas correctamente, accederás a la página de configuración del repositorio. Anota la URL de tu repositorio(https://github.com/<tunombredeusuario>/PruebasFuncionales.git). La página también te dará una serie de instrucciones para enviar tu código al repositorio utilizando comandos Git. Tendrás que instalar y configurar Git en tu máquina para ejecutarlos.

Para ello, sigue estos pasos:

  1. Descarga e instala Git desde tu símbolo del sistema utilizando los siguientes comandos:

    // macOS
    $ brew install git
    // Linux
    $ sudo apt-get install git

    Si utilizas Windows, descarga el instalador desde el sitio oficial de Git para Windows.

  2. Verifica la instalación ejecutando el siguiente comando:

    $ git --version
  3. Siempre que hagas un commit, es necesario vincularlo a un nombre de usuario y a una dirección de correo electrónico con fines de seguimiento. Proporciona los tuyos a Git con estos comandos para que los adjunte automáticamente cuando hagas una confirmación:

    $ git config --global user.name "yourUsername"
    $ git config --global user.email "yourEmail"
  4. Verifica la configuración ejecutando este comando:

    $ git config --global --list

Flujo de trabajo

El flujo de trabajo en Git tiene cuatro etapas por las que se moverá tu código, como se ve en la Figura 4-7. Cada etapa tiene un propósito diferente, como aprenderás.

Git workflow with four stages
Figura 4-7. Flujo de trabajo Git con cuatro etapas

La primera etapa es tu directorio de trabajo, donde realizas cambios en tu código de prueba (añades nuevas pruebas, arreglas guiones de prueba, etc.). La segunda etapa es el área de preparación local a la que añades cada pequeño trozo de trabajo, como la creación de una clase de página, a medida que lo terminas. Esto te permite hacer un seguimiento de los cambios que vas haciendo para poder revisarlos y reutilizarlos más adelante. La tercera etapa es tu repositorio local. Como se ha mencionado antes, Git proporciona a todo el mundo una copia de todo el repositorio junto con el historial en su máquina local. Una vez que tengas una estructura de pruebas que funcione, puedes hacer un commit que moverá todo lo que hayas añadido a la zona de pruebas a tu repositorio local. Esto facilita la reversión de todo el código como un único trozo cuando se produzcan fallos. Una vez que hayas terminado con todos los cambios necesarios -en este caso, cuando hayas completado una prueba y quieras que se ejecute como parte de la tubería CI- puedes enviarla al repositorio remoto. La nueva prueba también estará disponible para todos los miembros de tu equipo.

Los comandos Git para mover el código a través de las distintas etapas se muestran en la Figura 4-7. Puedes probarlos ahora paso a paso como se indica a continuación:

  1. En tu terminal, ve a la carpeta donde creaste tus pruebas automatizadas de Selenium en el Capítulo 3. Ejecuta el siguiente comando para inicializar el repositorio Git:

    $ cd /path/to/project/
    $ git init

    Este comando creará la carpeta .git en tu directorio de trabajo actual.

  2. Añade todo tu conjunto de pruebas al área de preparación ejecutando el siguiente comando:

     $ git add .

    En su lugar, puedes añadir un archivo (o directorio) concreto con git add filename.

  3. Consigna tus cambios en el repositorio local con un mensaje legible que explique el contexto de la consignación ejecutando el siguiente comando con el texto de mensaje adecuado:

     $ git commit -m "Adding functional tests"

    Puedes combinar los pasos 2 y 3 añadiendo al parámetro opcional -a; es decir, git commit -am "message".

  4. Para enviar tu código al repositorio público, primero debes proporcionar su ubicación a tu Git local. Para ello, ejecuta el siguiente comando:

     $ git remote add origin
      https://github.com/<yourusername>/FunctionalTests.git
  5. El siguiente paso es enviarlo al repositorio público. Tienes que autenticarte proporcionando tu nombre de usuario de GitHub y tu código de acceso personal al enviar. Un token de acceso personal es una contraseña de corta duración que GitHub exige para todas las operaciones a partir de agosto de 2021, por motivos de seguridad. Para obtener tu token de acceso personal, ve a tu cuenta de GitHub, navega hasta Configuración → Configuración de desarrollador → "Tokens de acceso personal", haz clic en "Generar nuevo token" y rellena los campos requeridos. Utiliza el token cuando se te solicite después de ejecutar el siguiente comando:

     $ git push -u origin master
    Consejo

    Si no quieres autenticarte cada vez que interactúes con el repositorio público, puedes optar por configurar el mecanismo de autenticación SSH.

  6. Abre tu cuenta de GitHub y verifica el repositorio.

Cuando trabajes con miembros del equipo, tendrás que extraer su código a tu máquina desde el repositorio público. Puedes hacerlo ejecutando el comando git pull. Si ya tienes un repositorio de pruebas funcionales para tu equipo, puedes utilizar git clone repoURL para obtener tu copia del repositorio local en lugar de git init.

Otros comandos de Git, como git merge, git fetch, y git reset, nos hacen la vida más fácil. Explóralos en la documentación oficial cuando sea necesario.

Jenkins

El siguiente paso es configurar un servidor Jenkins CI en tu máquina local e integrar las pruebas automatizadas de tu repositorio Git.

Nota

La intención de esta parte del ejercicio es que comprendas cómo se pueden implementar en la práctica las pruebas continuas utilizando herramientas de CI/CD, no enseñarte DevOps. Los equipos pueden contratar a desarrolladores con conocimientos especializados de DevOps o tener una función de DevOps para gestionar el trabajo de creación y mantenimiento de canalizaciones CI/CD/CT. Sin embargo, es esencial que tanto los desarrolladores como los probadores estén familiarizados con el proceso CI/CD/CT y su funcionamiento, ya que interactuarán con este proceso y depurarán los fallos de primera mano. Además, desde el punto de vista de las pruebas, es fundamental aprender a adaptar el proceso de CT a las necesidades específicas del proyecto y asegurarse de que las fases de prueba se encadenen correctamente, según la estrategia de CT del equipo.

Configurar

Jenkins es un servidor CI de código abierto. Para utilizarlo en , descarga el paquete de instalación para tu sistema operativo y sigue el procedimiento de instalación estándar. Una vez instalado, inicia el servicio Jenkins. En macOS, puedes instalar e iniciar el servicio Jenkins utilizando los comandos brew como se indica a continuación:

$ brew install jenkins-lts
$ brew services start jenkins-lts

Cuando el servicio se haya iniciado correctamente, abre la interfaz web de Jenkins en http://localhost:8080/. El sitio te guiará a través de las siguientes actividades de configuración:

  1. Desbloquea Jenkins con una contraseña de administrador única que se generó como parte del proceso de instalación. La página web te mostrará la ruta a la ubicación de esta contraseña en tu máquina local.

  2. Descarga e instala los plug-ins de Jenkins más utilizados.

  3. Crea una cuenta de administrador. Con esta cuenta entrarás siempre en Jenkins.

Después de la configuración inicial, se te llevará a a la página del Panel de Jenkins, como se ve en la Figura 4-8.

Jenkins Dashboard view
Figura 4-8. Vista del panel de Jenkins
Nota

Aunque para este ejercicio estás configurando un servidor de CI en tu máquina local, en la práctica el servidor de CI estará alojado en la nube o en una máquina virtual en la misma red para que todos los miembros del equipo de puedan acceder a él.

Flujo de trabajo

Ahora, sigue estos pasos para configurar una nueva canalización para tus pruebas automatizadas:

  1. Desde el Panel de Control de Jenkins, ve a Gestionar Jenkins → Configuración Global de la Herramienta para configurar las variables de entorno JAVA_HOME y MAVEN_HOME, como se ve en 4-9 y 4-10. Puedes escribir el comando mvn -v en tu terminal para obtener ambas ubicaciones.

    Configuring JAVA_HOME in Jenkins
    Figura 4-9. Configuración de JAVA_HOME en Jenkins
    Configuring MAVEN_HOME in Jenkins
    Figura 4-10. Configuración de MAVEN_HOME en Jenkins
  2. Volviendo a la vista Panel de control, selecciona la opción Nuevo elemento en el panel izquierdo para crear una nueva canalización. Introduce un nombre para la canalización, por ejemplo "Pruebas funcionales", y elige la opción "Proyecto de estilo libre". Esto te llevará a la página de configuración de la canalización, como se ve en la Figura 4-11.

    The Jenkins pipeline configuration page
    Figura 4-11. Página de configuración del canal de Jenkins
  3. Introduce los siguientes datos para configurar tu canalización:

    • En la pestaña General, añade una descripción de la canalización. Selecciona "Proyecto GitHub" e introduce la URL de tu repositorio (sin la extensión .git ).

    • En la pestaña Gestión del Código Fuente, selecciona Git e introduce la URL de tu repositorio (esta vez con la extensión .git ). Jenkins lo utilizará para hacer git clone.

    • La pestaña Desencadenantes de la compilación proporciona algunas opciones para configurar cuándo y cómo poner en marcha el canal de forma automatizada . Por ejemplo, la opción Sondear SCM puede utilizarse para sondear el repositorio Git cada dos minutos para comprobar si hay nuevos cambios y, si los hay, iniciar la ejecución de la prueba. La opción Construir periódicamente puede utilizarse para programar la ejecución de la prueba a intervalos fijos, aunque no haya nuevos cambios en el código. Esto puede utilizarse para configurar regresiones nocturnas. Del mismo modo, la opción "GitHub hook trigger for GITScm polling" configura un plug-in de GitHub para que envíe un trigger a Jenkins cada vez que haya nuevos cambios. Para simplificarlo, elige Sondear SCM e introduce este valor para sondear el repositorio de pruebas funcionales cada dos minutos: H/2 * * * *.

    • Como tu marco de pruebas funcionales Selenium WebDriver utiliza Maven, selecciona la opción "Invocar objetivos Maven de nivel superior" en la pestaña Construir. Elige tu Maven local, que configuraste en el Capítulo 3. En el campo Objetivos, introduce la fase del ciclo de vida de Maven que debe ejecutar la canalización: test. Esto ejecutará el comando mvn test desde el directorio del proyecto.

    • La pestaña Acciones posteriores a la compilación es donde puedes encadenar varios pipelines, es decir, activar el pipeline de pruebas CFR después de que haya pasado el pipeline de pruebas funcionales y crear un pipeline CD completo.4

  4. Guarda y navega hasta la vista Panel de Control. Verás la canalización creada, como se ve en la Figura 4-12.

    Your pipeline in the Jenkins Dashboard
    Figura 4-12. Tu canalización en el Panel de Jenkins
  5. Haz clic en el nombre de la canalización en la vista Panel de control y, en la página de destino, selecciona la opción Construir ahora en el panel izquierdo. La canalización clonará el repositorio en tu máquina local y ejecutará el comando mvn test. Puedes ver que el navegador Chrome se abre y se cierra como parte de la ejecución de la prueba.

  6. Localiza la carpeta Espacio de trabajo en la misma página. En esta carpeta encontrarás la copia local clonada del código del repositorio y los informes generados tras la ejecución de las pruebas; puedes utilizarla para depurar.

  7. En la parte inferior del panel izquierdo de la misma página, selecciona el recuento de construcciones de la ejecución actual del pipeline. Tendrás la opción de ver la salida de la consola en el panel izquierdo de la página de inicio. Esta vista mostrará las actividades de ejecución en directo para su depuración.

Enhorabuena, ¡eso completa tu configuración CI!

En la misma línea, tendrás que añadir las etapas de las pruebas respectivas (código estático, aceptación, humo, CFR) según tu estrategia de pruebas continuas para completar la configuración de CD de extremo a extremo del proyecto. Asegúrate de que las etapas se activan no sólo tras los cambios en el código de la aplicación , sino también tras los cambios en la configuración, la infraestructura y el código de prueba.

Las cuatro métricas clave

El resultado final de todo este esfuerzo de dedicado a establecer tus procesos de CI/CD/CT (y a cumplir los principios y la etiqueta expuestos anteriormente) es que el equipo se califique como equipo de élite o de alto rendimiento según las cuatro métricas clave (4KM) identificadas por el equipo de Investigación y Evaluación de DevOps (DORA) de Google. DORA formuló las 4KM basándose en una amplia investigación, y esbozó cómo utilizar estas métricas para cuantificar el nivel de rendimiento de un equipo de software como élite, alto, medio o bajo. El libro Accelerate de Jez Humble, Gene Kim y Nicole Forsgren es una lectura excelente para conocer los detalles de la investigación.

En resumen, las cuatro métricas clave nos permiten medir el ritmo de entrega de un equipo y la estabilidad de sus lanzamientos. Son las siguientes

Plazo de entrega

El tiempo transcurrido desde que se confirma el código hasta que está listo para su implementación en producción.

Frecuencia de Implementación

La frecuencia con la que el software se implementa en producción o en una tienda de aplicaciones.

Tiempo medio de restauración

El tiempo que se tarda en restablecer cualquier interrupción del servicio o en recuperarse de los fallos

Cambio porcentaje suspenso

El porcentaje de cambios liberados a producción que requieren una reparación posterior, como retrocesos a una versión anterior o correcciones en caliente, o que causan una degradación de la calidad del servicio.

Las dos primeras métricas, el plazo de entrega y la frecuencia de implementación, exponen el ritmo de entrega del equipo. Miden la rapidez con la que un equipo puede entregar valor a los usuarios finales y la frecuencia con la que añade valor a los usuarios finales. Sin embargo, en la prisa por ofrecer valor a los clientes, el equipo no debe comprometer la estabilidad del software. Las dos últimas métricas validan esto. El tiempo medio de restauración y el porcentaje de fallos de cambio proporcionan una indicación de la estabilidad del software que se está lanzando. En el mundo actual, los fallos de software son inevitables, y estas métricas miden lo fácil que es recuperarse de estos fallos y la frecuencia con que se producen debido a nuevas versiones. Como puedes ver, juntos, los 4KM dan una imagen clara del rendimiento de un equipo de software, midiendo su velocidad, reactividad y capacidad de entregar con calidad y estabilidad.

Los objetivos de un equipo de élite, según la investigación DORA, están representados en la Tabla 4-1.

Tabla 4-1. Las cuatro métricas clave de un equipo de élite
Métrica Objetivo
Frecuencia de Implementación Bajo demanda (múltiples implementaciones al día)
Plazo de entrega Menos de un día
Tiempo medio de restauración Menos de una hora
Cambio porcentaje suspenso 0-15%

Como ya hemos comentado, una de las principales ventajas de tener un proceso CI/CD/CT riguroso es que tu equipo podrá ofrecer valor a los clientes bajo demanda. Del mismo modo, como has visto, cuando colocas pruebas automatizadas en las capas adecuadas de la aplicación, puedes tener tu código probado como parte del proceso de pruebas continuas y fácilmente listo para su implementación en cuestión de horas (es decir, tu plazo de entrega será inferior a un día). Además, con tus pruebas de requisitos funcionales y multifuncionales automatizadas y ejecutadas como parte del proceso de TC, no debería ser ningún problema mantener tu porcentaje de cambios fallidos dentro del rango recomendado del 0-15%. Por tanto, el esfuerzo que realices en este frente permitirá a tu equipo obtener el estatus de "élite", según la definición de DORA. La investigación de DORA también demuestra que los equipos de élite contribuyen al éxito de una organización, en términos de beneficios, cotización, retención de clientes y otros criterios. Y cuando a la organización le va bien, cuida bien de sus empleados, ¿verdad?

Puntos clave

Éstos son los puntos clave de este capítulo:

  • El proceso de pruebas continuas valida la calidad de la aplicación, tanto en los aspectos funcionales como en los interfuncionales, de forma automatizada para cada cambio incremental.

  • Las pruebas continuas dependen en gran medida del proceso de integración continua. La integración y las pruebas continuas, a su vez, permiten la entrega continua de software a los clientes bajo demanda.

  • Los procesos de integración y pruebas continuas exigen que los equipos sigan unos principios y una etiqueta estrictos para que sean fructíferos.

  • Planifica tu proceso de pruebas continuas de forma que obtengas retroalimentación rápida en múltiples bucles continuamente.

  • Las ventajas de las pruebas continuas son numerosas, y muchas de ellas -como el establecimiento de objetivos comunes de calidad entre funciones y equipos, la propiedad compartida de la entrega y la mejora de la colaboración entre equipos distribuidos- son difíciles de conseguir de otro modo.

  • Aunque los ingenieros de DevOps puedan ser responsables de la configuración y el mantenimiento de CI/CD, es vital que los probadores del equipo diseñen la estrategia de pruebas continuas y se aseguren de que los bucles de retroalimentación se activan correctamente. Y lo que es más importante, deben vigilar de cerca las prácticas de TC del equipo para garantizar que el esfuerzo invertido en crear y mantener pruebas coseche los beneficios adecuados.

  • Seguir procesos rigurosos de CI/CD/CT llevará a tu equipo a convertirse en un equipo de élite, según la definición de la investigación DORA. ¡Y un equipo de élite contribuye al éxito de toda la organización!

1 Para más información sobre éste y otros principios de la industria de CI/CD comúnmente prescritos, véase The DevOps Handbook (IT Revolution Press), de Gene Kim, Jez Humble, Patrick Debois y John Willis.

2 Jez Humble y David Farley tratan más extensamente estas técnicas de optimización en Entrega Continua.

3 Para más detalles, consulta el libro de Jez Humble, Gene Kim y Nicole Forsgren, Accelerate (IT Revolution Press).

4 Para más información sobre cómo trabajar con canalizaciones, consulta la documentación de Jenkins.

Get Pruebas Full Stack now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.