Configurar varios perfiles de test en Spring Boot
26-03-2024
Cualquiera que haya trabajado con Spring Boot sabe que se pueden configurar varios perfiles para distintos entornos de ejecución de la aplicación.
Es algo común en proyectos reales, por ejemplo, para establecer el acceso a distintas bases de datos como la del entorno productivo y la de staging/pre-producción de forma que no haya que hacer cambios en el resto de código.
Lo que no se me había ocurrido era que esto también puede hacerse en el package de test.
¿Cómo configurarlo?
La configuración de perfiles en el application.yml
del package de test, se hace igual que en su homónimo de código
fuente:
- Se separan los distintos perfiles con el separador
--
- Se define a qué perfil corresponde cada configuración específica con
spring:config:activate:on-profile: <nombre del perfil>
De esta forma, podemos decirle a Spring Boot qué perfil queremos que use para ejecutar los tests con una variable de entorno.
Un pequeño truco para seguir ejecutando los tests en nuestro entorno local como hasta ahora, sin tener que especificar
la nueva variable de entorno, es establecer un valor por defecto para ésta en el fichero de configuración con la
sintaxis ${<VARIABLE_DE_ENTORNO>:<valor por defecto>}
.
Un ejemplo de cómo quedaría un application.yml
de test con un perfil para el entorno de CI que usa un datasource
diferente:
### Default configurations ###
spring:
application:
name: Our cool backend app
profiles:
active: ${TEST_PROFILE:test} # Env variable's default value syntax
---
### TEST ###
spring:
config:
activate:
on-profile: test
datasource:
driver-class-name: org.testcontainers.jdbc.ContainerDatabaseDriver
url: jdbc:tc:mysql:8.0.32:///${DATABASE_NAME}
username: ${ROOT_USER}
password: ${ROOT_PASSWORD}
---
### JENKINS ###
spring:
config:
activate:
on-profile: jenkins
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://${DATABASE_HOST}:${DATABASE_PORT}/${DATABASE_NAME}
username: ${ROOT_USER}
password: ${ROOT_PASSWORD}
Vale, pero… ¿para qué puede servir esto?
En el ejemplo anterior ya se deja caer un pequeño spoiler, pero el caso real que me hizo aprender que esto era posible, fue encontrarme en un proyecto con un entorno de CI con una particularidad que impedía ejecutar nuestros tests de integración: TestContainers en Kubernetes.
Si tenemos nuestros tests de integración planteados con una base de datos volátil montada en TestContainers, y en la compañía tienen configurada una herramienta de CI como Jenkins para ejecutarse sobre pods de Kubernetes, podemos tener problemas para que funcionen nuestros tests al ejecutar la pipeline del proyecto.
Esto pasa porque el contenedor donde se están ejecutando los tests, no tiene acceso a un daemon de Docker en el que iniciar TestContainers y, aunque se pueden hacer apaños como lo que llaman “Docker in Docker” (tener acceso a otro contenedor en el que se esté ejecutando un daemon de Docker), sería necesario darle permisos de administrador a nuestro contenedor inicial, lo que nunca es recomendable desde el punto de vista de la seguridad.
Configurar un perfil exclusivo para esta CI nos permitió seguir el desarrollo con TestContainers en nuestro entorno local, preparando una base de datos diferente en el entorno de la pipeline para evitar falsos tests de integración fallidos.
De no ser así, habríamos tenido que montar una base de datos local extra para ejecutar los tests solo por satisfacer los requisitos de la CI, o limitar la expresividad del dialecto SQL que estábamos usando, al recurrir a una base de datos en memoria como H2.
Aunque siempre hay soluciones alternativas, nunca está de más conocer cualquier herramienta que pueda agilizar el setup de un proyecto, nos evite un extra de consumo de espacio en disco y haga la experiencia de desarrollo más amigable.