Tests de integración automáticos Docker, Flyway y Maven

0
1822
java automatic integration tests

Índice de contenidos

Introducción

Crear tests de integración totalmente automáticos es el objetivo de muchos desarrolladores. Hasta no hace mucho, las bases de datos embebidas en memoria nos ofrecían la ventaja de atomizar completamente nuestros tests sin perder la capacidad de integrar con bases de datos (o cualquier otro servicio). Sin embargo, a las buenas o a las malas, muchos ya hemos aprendido que usar bases de datos en memoria no es lo mejor en cuanto a tests de integración se refiere.

Gracias a Docker, todo esto se vuelve mucho más sencillo. Levantar una base de datos realmente idéntica a la real se ha convertido en una nimiedad. Y, cuando se trata de ejecutar todo el histórico de scripts para actualizar una base de datos a la última versión, Flyway nos ofrece automatización total.

Como es bien sabido, Maven cuenta con infinidad de plugins entre los que no podían faltar plugins para Docker y Flyway. Aquí, en Adictos al trabajo, ya os hemos hablado de algunos plugins para docker y cómo configurarlos. Lo mismo pasa con Flyway, que ya es un viejo conocido en este sitio. Así que no voy a repetir lo mismo. Pero vamos a ir un pasito más allá.

Objetivo

Sí, crear una base de datos es muy sencillo con docker. En muchos entornos de desarrollo, tienes que levantarte tu base de datos (aunque se aún docker) y ejecutar tus scripts para crear los esquemas y actualizarla. Parece una tontería pero, ¿y si automatizamos todo esto? Imagínate descargarte el entorno de un proyecto y poder ejecutar los tests de integración automáticos solo con tener el daemon de docker en ejecución.

Docker

Para este ejemplo voy a utilizar un plugin diferente a los que ya habíamos visto antes: io.fabric8/docker-maven-plugin.

Tiene muy buena documentación, así que no te olvides de echarle un vistazo para conocer este plugin más en profundidad y poder adaptarlo perfectamente a tu proyecto.

La configuración que voy a utilizar yo es la siguiente:

<plugin>
  <groupId>io.fabric8</groupId>
  <artifactId>docker-maven-plugin</artifactId>
  <version>${docker-maven-plugin.version}</version>
  <executions>
    <execution>
      <id>prepare-it-database</id>
      <phase>pre-integration-test</phase>
      <goals>
        <goal>start</goal>
      </goals>
      <configuration>
        <images>
          <image>
            <name>mysql:5.7</name>
            <alias>mysql</alias>
            <run>
              <ports>
                <port>${mysql.integrationTests.port}:3306</port>
              </ports>
              <env>
                <MYSQL_ROOT_PASSWORD>root</MYSQL_ROOT_PASSWORD>
                <MYSQL_DATABASE>${flyway.db.name}</MYSQL_DATABASE>
                <MYSQL_USER>${flyway.db.user}</MYSQL_USER>
                <MYSQL_PASSWORD>${flyway.db.password}</MYSQL_PASSWORD>
              </env>
              <log>
                <date>long</date>
                <color>green</color>
              </log>
              <wait>
                <time>15000</time>
              </wait>
            </run>
          </image>
        </images>
      </configuration>
    </execution>
    <execution>
      <id>remove-it-databse</id>
      <phase>post-integration-test</phase>
      <goals>
        <goal>stop</goal>
      </goals>
    </execution>
  </executions>
</plugin>

Esta configuración levantará un contenedor con la imagen mysql v5.7 en el puerto especificado.

En la sección <env> configuramos las variables de entorno para nuestro contenedor, tal y como haríamos con el flag -e al ejecutar nuestro contenedor por terminal.

La ejecución para levantar el contenedor se dará en la fase pre-integration-test y, en la fase post-integration-test, se parará y eliminará el contenedor. El proyecto usado para el ejemplo utiliza el plugin failsafe para controlar la ejecución de los tests de integración, por eso tenemos disponibles estas fases para antes y después de los tests de integración. Si en tu caso no es así, deberás adaptarlo para que funcione en tu caso concreto.

Un detalle que no quiero pasar por alto, aunque parezca ridículo, es el <wait> en el que especificamos que espere 15 segundos. Para ello debemos entender cómo funciona la imagen de mysql para Docker: al ejecutar el comando que arranca el contenedor, tarda apenas unos milisegundos en devolver la respuesta existosa. Pero esto significa solo que el contenedor se ha creado satisfactoriamente. Sin embargo, el servicio de mysql dentro de este contenedor todavía no ha terminado, ya que está ejecutando lo que le hemos indicado en las variables de entorno (crear la base de datos y el usuario con permisos).

En principio esto no siempre debe suponer un problema. No obstante, ese usuario, contraseña y base de datos son necesarios para que, justo después, actúe Flyway con la configuración que veremos más adelante. Si dejamos al plugin actuar inmediatamente, a velocidad de máquina, lo que pasará es que dará error porque el servicio de mysql todavía no habrá terminado de crear la base de datos y el usuario para Flyway, por lo que no podrá conectarse. Además, durante ese tiempo indicado en milisegundos, veremos la terminal del contenedor, tal y como está configurado en el apartado <log>. Y esto siempre viene bien para controlar si todo ha ido como debería.

Algunos otros detalles que viene bien tener en cuenta:

  • No elimina volúmenes por defecto. Existe la opción que permite eliminarlos y, si utilizamos volúmenes, debemos configurar el plugin adecuadamente.
  • Tampoco lo hace con las imágenes y las networks.
  • Por el funcionamiento de Maven, si el proyecto tiene submódulos que heredan la misma configuración a través del pluginManagement, debemos tener en cuenta que, se ejecuten o no tests de integración, siempre se levantará el contenedor en cada módulo. Por tanto, debemos controlar según el perfil la fase en la que se ejecuta el plugin. Aunque estemos funcionando, por ejemplo, con un perfil que no ejecuta tests de integración, la fase de los tests de integración no deja de existir, lo único es que failsafe «skipeará» los tests. Por tanto, como la fase sigue existiendo, el contenedor se levantará siempre. Un truco es, según el perfil, marcar la fase de ejecución a none para que no se levante. Aunque también existe el atributo skip que puedes configurar según el perfil.

Por último, la mejor recomendación que puedo hacerte acerca del plugin para Docker, es que visites su buena documentación y desarrolles la configuración que más sentido tenga para tu proyecto.

Flyway

Lo siguiente, tras levantar la base de datos, es configurar flyway para que ejecute todo el histórico y deje el esquema correcto preparado para lanzar nuestros tests de integración. Como os dije al principio, no es la primera vez que os hablamos de cómo combinar Flyway y Maven. Además, cuenta con una buena documentación de obligada lectura para poder sacarle el máximo provecho.

Os dejo un ejemplo para poder trabajar en conjunto con Docker:

<plugin>
    <groupId>org.flywaydb</groupId>
    <artifactId>flyway-maven-plugin</artifactId>
    <version>${flyway.version}</version>
    <configuration>
        <url>${dbUrlFlyway}</url>
        <user>${flyway.db.user}</user>
        <password>${flyway.db.password}</password>
        <locations>
            <location>${flyway.location.before}</location>
            <location>${flyway.location.profile}</location>
            <location>${flyway.location.after}</location>
        </locations>
    </configuration>
    <executions>
        <execution>
            <id>flyway</id>
            <phase>pre-integration-test</phase>
            <goals>
                <goal>clean</goal>
                <goal>migrate</goal>
            </goals>
        </execution>
    </executions>
    <dependencies>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>${mysql.version}</version>
        </dependency>
    </dependencies>
</plugin>

Con esta configuración, cargará los archivos ubicados en <locations> a nuestra base de datos según la configuración que le hemos indicado (usuari, contraseña…).

En este caso hemos configurado los goals clean y migrate. Normalmente es solo necesario con migrate, pero así os mostrábamos cómo es posible ejecutar en ese orden más de un goal.

Otras recomendaciones

Ambos plugins están configurados para ejecutarse en la fase pre-integration-test. Se ejecutarán por orden: si declaras primero flyway, intentará lanzarse y, como no estará todavía preparado docker, no funcionará.

Si tienes varios módulos que ejecutan tests de integración, por el propio funcionamiento de maven, creará y destruirá el contenedor según vaya construyendo cada módulo. Por tanto, si puedes agrupar todo en un módulo, ahorrarás tiempo de ejecución.

Conclusión

Automatizar al máximo los tests de integración siempre ha sido un TO DO en nuestra lista de desarrolladores. Hemos evolucionado y, a las buenas o a las malas, hemos aprendido que «mockear» nuestra base de datos en memoria no es lo más adecuado para los tests de integración. Gracias a Docker y a herramientas de control de versión para bases de datos como Flyway, hemos conseguido poder ejecutar los tests de integración de forma totalmente automática, sin tener levantada previamente ninguna instancia preparada de base de datos.

DEJA UNA RESPUESTA

Por favor ingrese su comentario!

He leído y acepto la política de privacidad

Por favor ingrese su nombre aquí

Información básica acerca de la protección de datos

  • Responsable:
  • Finalidad:
  • Legitimación:
  • Destinatarios:
  • Derechos:
  • Más información: Puedes ampliar información acerca de la protección de datos en el siguiente enlace:política de privacidad