Primeros pasos con la integración continua

Conoce cómo adoptar la integración continua y las pruebas automatizadas en cinco pasos.

Sten Pittet Sten Pittet

La integración continua (CI) es una práctica recomendada propia de las metodologías ágil y DevOps en la que los desarrolladores integran los cambios de código desde el primer momento y de manera regular en la rama principal o el repositorio de código. El objetivo es reducir el riesgo de enfrentarse al "infierno de la integración" esperando a que llegue el final de un proyecto o un sprint para fusionar el trabajo de todos los desarrolladores. Dado que la CI automatiza la implementación, ayuda a los equipos a cumplir los requisitos empresariales, a mejorar la calidad del código y a incrementar el nivel de seguridad.

Una de las grandes ventajas de adoptar CI es que te ahorrará tiempo durante el ciclo de desarrollo, al identificar y abordar conflictos desde las etapas más tempranas. También es una gran manera de reducir el tiempo dedicado a la corrección de errores y la regresión, al poner más énfasis en contar con un buen conjunto de pruebas. Por último, ayuda a extender el conocimiento del código base y las funciones que estás desarrollando para los clientes.

El primer paso en tu camino hacia la integración continua es configurar pruebas automatizadas.

Introducción a las pruebas automatizadas

Conocer los distintos tipos de pruebas

Para disfrutar de todas las ventajas de la IC, deberás automatizar tus pruebas para poder ejecutarlas para cada cambio realizado en el repositorio principal. Insistimos en que se deben ejecutar pruebas en cada rama del repositorio y no solo centrarse en la rama principal. De esta manera, podrás detectar incidencias a tiempo y minimizar las interrupciones que pueda sufrir tu equipo.

Hay muchos tipos de pruebas implementadas, pero no es necesario hacer todo a la vez si acabas de empezar. Puedes comenzar con unas pruebas unitarias e ir ampliando la cobertura con el tiempo.

  • Las pruebas unitarias son de alcance limitado y suelen verificar el comportamiento de métodos o funciones individuales.
  • Las pruebas de integración garantizan que varios componentes se comporten de forma correcta en conjunto. Esto puede involucrar varias clases, así como pruebas de integración con otros servicios.
  • Las pruebas de aceptación son similares a las de integración, pero se centran en los casos de negocio más que en los componentes en sí.
  • Las pruebas de interfaz de usuario permiten asegurarte de que la aplicación funciona correctamente desde la perspectiva del usuario.

No todas las pruebas son iguales, y puedes ver las compensaciones que harás con la pirámide de pruebas desarrollada por Mike Cohn.

Triángulo de pruebas

Las pruebas unitarias son rápidas y baratas de implementar, ya que en su mayoría hacen comprobaciones en pequeñas piezas de código. Por otro lado, la implementación de las pruebas de interfaz de usuario será compleja y su ejecución, lenta. Esto es así porque suelen necesitar un entorno completo y múltiples servicios para emular comportamientos en navegadores o dispositivos móviles. Por esto, puede que quieras limitar el número de pruebas de interfaz de usuario complejas y confiar en buenas pruebas unitarias para agilizar la compilación y dar feedback a los desarrolladores lo antes posible.

Ejecutar tus pruebas automáticamente

Para adoptar la integración continua, deberás ejecutar tus pruebas cada vez que se envíe un cambio a la rama principal. Para ello, deberás contar con un servicio que pueda supervisar tu repositorio y que esté atento a los nuevos envíos al código base. Hay muchas soluciones para elegir, tanto locales como en la nube. Para elegir servidor, hazte estas preguntas:

  • ¿ Dónde se aloja tu código? ¿El servicio de CI puede acceder a tu código base? ¿Tienes alguna restricción especial sobre dónde puede residir el código?
  • ¿ Qué sistema operativo y que recursos necesitas para la aplicación? ¿Es compatible tu entorno de aplicación? ¿Puedes instalar las dependencias adecuadas para compilar y probar el software?
  • ¿Cuántos recursos necesitas para las pruebas? Algunas aplicaciones en la nube pueden limitar los recursos que puedes utilizar. Si tu software consume muchos recursos, puede que quieras alojar el servidor de CI detrás del firewall.
  • ¿Cuántos desarrolladores hay en tu equipo? Si tu equipo aplica CI, cada día se enviarán muchos cambios al repositorio principal. Para que los desarrolladores reciban feedback rápidamente, tienes que reducir el tiempo de cola de las compilaciones y querrás contar con un servicio o un servidor que te ofrezcan la simultaneidad correcta.

Antes tenías que instalar un servidor de IC independiente, como Bamboo o Jenkins, pero ahora puedes encontrar soluciones en la nube mucho más sencillas de adoptar. Por ejemplo, si tu código está alojado en Bitbucket Cloud, puedes ver la función de Pipelines en el repositorio para ejecutar pruebas con cada envío sin tener que configurar un servidor aparte ni agentes de compilación, y sin restricciones de concurrencia.

image: node:4.6.0 pipelines:   default:     - step:         script:           - npm install           - npm test

Ejemplo de configuración de pruebas de un repositorio de JavaScript con Bitbucket Pipelines.

Uso de la cobertura de código para encontrar código no sometido a pruebas

Cuando adoptes pruebas automatizadas, es recomendable combinarlas con una herramienta de cobertura de pruebas que te permita saber qué parte del código base cubre tu conjunto de pruebas.

Está bien aspirar a una cobertura superior al 80 %, pero hay que tener cuidado con no confundir un porcentaje de cobertura elevado con un buen conjunto de pruebas. Una herramienta de cobertura de código te ayudará a encontrar código no sometido a pruebas, pero es la calidad de las pruebas la que marcará la diferencia al fin y al cabo.

Si acabas de empezar, no tengas prisa por alcanzar una cobertura del 100 % del código base. En lugar de eso, utiliza una herramienta de cobertura que te permita identificar partes críticas de la aplicación que aún no tienen pruebas, y empieza por ahí.

La refactorización da la oportunidad de añadir pruebas

Si estás a punto de realizar cambios significativos en tu aplicación, deberías empezar escribiendo pruebas de aceptación alrededor de las funciones que pueden verse afectadas. Esto te dará una red de seguridad para asegurarte de que el comportamiento original no se ve afectado después de refactorizar código o de añadir nuevas funciones.

Adopción de la integración continua

Aunque automatizar tus pruebas es una parte importante de la CI, no basta. Es posible que debas cambiar la cultura del equipo para que los desarrolladores no trabajen durante días en una función sin fusionar los cambios en la rama principal; también tendrás que imponer la filosofía de las compilaciones con "luz verde".

Integra temprano y a menudo

Tanto si utilizas el desarrollo por tronco como ramas de funciones, es importante que los desarrolladores integren sus cambios lo antes posible en el repositorio principal. Si dejas que el código se quede en una rama o en la estación de trabajo del desarrollador durante mucho tiempo, corres el riesgo de tener demasiados conflictos cuando por fin decidas hacer la fusión en la rama principal.

Si la integración se hace desde el primer momento, se reduce el alcance de los cambios y será más fácil identificar los conflictos. La otra ventaja es que facilita el uso compartido del conocimiento entre desarrolladores, al hacer más absorbibles los cambios.

Si estás realizando cambios que pueden afectar a una función existente, puedes usar marcas de función para desactivar los cambios en producción hasta que termines tu trabajo.

Disfrutar de la aplicación correcta en todo momento

Si un desarrollador rompe la compilación de la rama principal, arreglarla es prioritario. Cuantos más cambios entren en la compilación mientras está rota, más difícil será determinar qué es lo que la ha roto (y también correrás el riesgo de introducir más fallos).

Merece la pena dedicar tiempo al conjunto de pruebas, para que los fallos salgan rápidamente y poder dar feedback lo antes posible al desarrollador que envió los cambios. Puedes dividir las pruebas para que las más rápidas (las pruebas unitarias, por ejemplo) se ejecuten antes que las de larga ejecución. Si tu conjunto de pruebas siempre tarda mucho tiempo en fallar, perderás tiempo de los desarrolladores, ya que tendrán que cambiar de contexto para retomar un trabajo anterior y resolver los errores.

Recuerda configurar notificaciones para que los desarrolladores reciban alertas en cuanto se rompa la compilación. Además, puedes dar un paso más allá y mostrar el estado de tus ramas principales en un panel que todos puedan ver.

Escribe pruebas dentro de tus historias

Por último, deberás asegurarte de que cada función que se desarrolle tenga pruebas automatizadas. Puede parecerte que esto ralentiza el desarrollo, pero, en realidad, reducirá drásticamente el tiempo que el equipo dedica a corregir regresiones o errores introducidos con cada iteración. También podrás hacer cambios en tu código base con confianza, ya que tu conjunto de pruebas podrá verificar rápidamente que todas las funciones desarrolladas anteriormente funcionan como se esperaba.

Para escribir buenas pruebas, tendrás que asegurarte de que los desarrolladores participan desde el principio en la definición de las historias de usuario. Es una manera excelente de conocer mejor los requisitos empresariales y facilitar la relación con los gestores de productos. Incluso puedes comenzar a escribir las pruebas antes de implementar el código al que se aplicarán.

Cuando corrijas errores, escribe pruebas

Tanto si tienes ya un código base como si acabas de empezar, seguro que tendrás errores en las publicaciones. Recuerda añadir pruebas cuando los resuelvas para evitar que vuelvan a ocurrir.

Con la CI, los ingenieros de control de calidad podrán escalar la calidad

Otra función que cambiará con la adopción de la CI y de la automatización es la de los ingenieros de control de calidad. Ya no tendrán que probar a mano capacidades poco relevantes de la aplicación y podrán dedicar más tiempo a proporcionar herramientas que respalden a los desarrolladores y a ayudarles a adoptar las estrategias de prueba adecuadas.

En cuanto empieces a adoptar la integración continua, los ingenieros de control de calidad podrán centrarse en facilitar pruebas con mejores herramientas y conjuntos de datos, y en ayudar a los desarrolladores a mejorar su capacidad de escribir mejor código. Seguirá habiendo pruebas exploratorias para casos de uso complejos, pero esto debería ser una parte menos relevante de su trabajo.

Integración continua en 5 pasos

Con esto, deberías tener unos conceptos básicos sobre la integración continua, que podrían reducirse a los siguientes puntos:

  1. Empieza escribiendo pruebas para las partes críticas de tu código base.
  2. Obtén un servicio de CI que ejecute esas pruebas automáticamente cada vez que se hace un envío al repositorio principal.
  3. Asegúrate de que tu equipo integra los cambios a diario.
  4. Corrige la compilación en cuanto se rompa.
  5. Escribe pruebas para cada nueva historia que implementes.

Aunque puede parecer sencillo, para que todo vaya bien necesitarás el compromiso del equipo. Al principio, tendrás que ralentizar tus publicaciones y necesitarás el apoyo de los propietarios del producto para que no metan prisa a los desarrolladores para lanzar funciones sin pruebas.

Te recomendamos comenzar poco a poco y con pruebas sencillas, para hacerte a la nueva rutina antes de pasar a implementar un conjunto de pruebas más complejo que pueda ser difícil de administrar.