El estado actual de las pruebas con Couchbase requiere que utilices algo como CouchbaseMocko imitar la API usted mismo, o tener una Servidor Couchbase iniciada antes de ejecutar esas pruebas. Mocking funciona pero no es realmente probar Couchbase. Podría estar bien para pruebas unitarias pero descartado para pruebas de integración. Tener una instancia de Couchbase iniciada es mejor. Y aunque esto funciona, no es una solución fácil e industrializable. No puedes simplemente ejecutar una compilación en cualquier máquina y esperar que la cosa funcione. Requeriría que todo el mundo instalara Couchbase Server y lo tuviera online todo el tiempo. Y aunque esto me haría muy feliz, es poco probable que suceda.
Así que otro enfoque podría ser tener su prueba responsable de iniciar la base de datos. Podrías incluirlo en tus scripts de construcción. Maven, Gradle y otros pueden proporcionarle ganchos en el ciclo de vida de construcción. De esta manera podrías iniciar y detener la base de datos antes de ejecutar la prueba de integración. Pero esto añadiría alguna dependencia de la herramienta de construcción que estés utilizando. Para ser independiente de la herramienta de construcción, necesitas arrancar y parar la BD desde tu código.
Otro problema que encontrarás al configurar esto es que probablemente tengas que soportar diferentes sistemas operativos. Iniciar y detener la base de datos será diferente si está ejecutando Linux, Windows u OSX. Y esto suponiendo que la BD ya esté instalada en la máquina. Para evitar estos problemas, necesitas un runtime común a todas estas plataformas. Y esto es algo que Docker puede darte.
Docker actuará como un almacén binario distribuido para descargar una imagen para cualquier BD que desees. Y la forma de gestionar el contenedor Docker es idéntica en todas las plataformas. Proporciona una alternativa superligera a las VMs y aunque puede darte problemas en algunos casos particulares en la producciónes absolutamente perfecto para este caso.
La gran gente que hay detrás Contenedores de prueba lo han comprendido y proponen una solución integrada para todos los problemas mencionados.
TestContainers es una biblioteca Java que soporta pruebas JUnit, proporcionando instancias ligeras y desechables de bases de datos comunes, navegadores web Selenium, o cualquier otra cosa que pueda ejecutarse en un contenedor Docker.
TestContainers es actualmente sólo Java pero este concepto puede ser adaptado a cualquier lenguaje que tenga un cliente Docker. Veamos cómo funciona con un simple proyecto Java.
Cómo probar la muestra de cerveza
Escribí un proyecto que muestra todo lo necesario para utilizar Contenedores de prueba y Couchbase. Para que este proyecto funcione necesitarás que Docker se ejecute en la máquina que ejecuta la prueba. Para asegurarte de que funciona, abre un terminal y escribe información de docker
. Esto debería darte una respuesta como esta:
Ahora vamos a escribir la prueba. Podemos empezar definiendo un ContenedorGenérico con una @ClassRule. Esto significa que el contenedor se configurará antes de ejecutar las pruebas y se desmontará después de las pruebas. Puede utilizar @Rule si desea que esto suceda para cada prueba de la clase que está ejecutando. Este GenericContainer requiere un nombre. Este es el identificador completo, nombre y etiqueta, de la imagen Docker que quieres usar. Aquí estoy usando una imagen personalizada que inicia Couchbase Server con la muestra beer precargada. Puedes construir esa imagen escribiendo docker build -t mycouchbase .
en la raíz del proyecto.
A continuación puedes configurar la lista de puertos que quieres exponer y una estrategia de espera. Esta parte de la estrategia de espera es bastante importante y volveremos a ella más adelante.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
público clase EjemploPrueba { @Regla de clase público estático ContenedorGenérico couchbase = nuevo ContenedorGenérico("mycouchbase:latest") .conPuertosExpuestos(8091, 8092, 8093, 8094, 11207, 11210, 11211, 18091, 18092, 18093) .waitingFor(nuevo CouchbaseWaitStrategy()); @Prueba público void beerBucketTest() lanza InterruptedException { CouchbaseEnvironment env = DefaultCouchbaseEnvironment.constructor() .bootstrapCarrierDirectPort(couchbase.getMappedPort(11210)) .bootstrapCarrierSslPort(couchbase.getMappedPort(11207)) .bootstrapHttpDirectPort(couchbase.getMappedPort(8091)) .bootstrapHttpSslPort(couchbase.getMappedPort(18091)) .queryPort(couchbase.getMappedPort(8093)) .construya(); CouchbaseCluster cc = CouchbaseCluster.crear(env); ClusterManager cm = cc.clusterManager("Administrador", "contraseña"); assertTrue(cm.hasBucket("muestra de cerveza")); Cubo cubo = cc.openBucket("muestra de cerveza"); assertTrue(cubo.existe("21st_amendment_brewery_cafe")); cubo.cerrar(); } |
Luego está el método de prueba. Empiezo definiendo un nuevo CouchbaseEnvironment. Dado que todos los puertos expuestos han sido mapeados a uno diferente, necesitamos especificarlos. Puedes obtener el puerto mapeado simplemente llamando a getMappedPort(yourPort) en el GenericContainer. Entonces simplemente creo mi CouchbaseCluster y sigo con mi test.
Esto sólo funciona porque he añadido una estrategia de espera personalizada llamando a waitingFor(new CouchbaseWaitStrategy())
. Por defecto TestContainers permite esperar a que un puerto sea accesible o a que una llamada a una URL devuelva un código de estado determinado. Desafortunadamente, esto no es suficiente para Couchbase. Cuando arrancas un servidor Couchbase hay una fase de calentamiento para los nodos de tu cluster. Un GET en http://couchabseserver:8091/ui/index.html
devolvería un código de estado 200 o el puerto 8091 respondería mientras tu nodo estaría aún en fase de calentamiento, por tanto inaccesible desde el SDK.
Esto significa que necesitamos una estrategia de espera específica para saber si el estado del nodo es saludable. Para saber si un nodo está en buen estado puedes obtener la siguiente URL http://couchabseserver:8091/pools/default/
. Devuelve un JSON con información sobre los nodos de tu cluster. Esto significa que necesitamos una estrategia de espera que obtenga ese JSON y compruebe si el estado del nodo es "saludable". Es como usar un HTTPWaitStrategy con algún comportamiento adicional.
Desafortunadamente esta clase está compuesta en su mayoría por campos privados y protegidos, lo que hace difícil extenderla en mi proyecto. Desafortunadamente tuve que duplicarla y añadir mi propia lógica. Puedes encontrar el código completo de la estrategia de espera en Github. Esta es la parte que he añadido.
1 2 3 4 5 6 7 8 |
// Estrategia de espera específica de Couchbase para asegurarse de que el nodo está en línea y sano JsonNode nodo = om.readTree(conexión.getInputStream()); JsonNode statusNode = nodo.en("/nodos/0/estado"); Cadena estado = statusNode.asText(); si (!"saludable".es igual a(estado)){ tirar nuevo RuntimeException(Cadena.formato("El estado del nodo Couchbase era: %s", estado)); } |
Conclusión
Este código sigue siendo bastante específico y requiere que construyas tu propia imagen de Couchbase, ya instalada y con datos. Tener imágenes prefabricadas con datos listos para pruebas de integración puede ser muy útil, especialmente para pruebas de integración. Sin embargo, es posible que desees tener un enfoque más ligero para las pruebas unitarias. TestContainers ya tiene varios módulos específicos para DBs particulares. Intentaremos hacer uno para Couchbase que no requiera una imagen en particular y te permita configurar nuestra imagen por defecto de la forma que quieras para tus tests.
Si desea este tipo de funciones, no dude en comunicárnoslo.