MongoDB integrado al ejecutar pruebas de integración

112

Mi pregunta es una variación de esta .

Dado que mi proyecto de aplicación web Java requiere muchos filtros de lectura / consultas e interfaces con herramientas como GridFS, estoy luchando por pensar en una forma sensata de emplear MongoDB de la manera que sugiere la solución anterior.

Por lo tanto, estoy considerando ejecutar una instancia incrustada de MongoDB junto con mis pruebas de integración. Me gustaría que se inicie automáticamente (ya sea para cada prueba o para toda la suite), vacíe la base de datos para cada prueba y se apague al final. Estas pruebas pueden ejecutarse en máquinas de desarrollo, así como en el servidor CI, por lo que mi solución también deberá ser portátil .

¿Alguien con más conocimiento sobre MongoDB puede ayudarme a tener una idea de la viabilidad de este enfoque y / o tal vez sugerir algún material de lectura que pueda ayudarme a comenzar?

También estoy abierto a otras sugerencias que la gente pueda tener sobre cómo podría abordar este problema ...

seanhodges
fuente
Si está utilizando maven, puede utilizar el nuestro mvnrepository.com/artifact/com.wenzani/mongodb-maven-plugin
markdsievers
También puede consultar este proyecto que simula un MongoDB dentro de la memoria JVM. github.com/thiloplanz/jmockmongo Pero todavía está en desarrollo.
Sebastien Lorber
No [solo para] las pruebas de unidades, sino que lea esta publicación de blog si desea ejecutar MongoDB (incluso un clúster) como implementación en memoria si está usando Linux. edgystuff.tumblr.com/post/49304254688 Sin embargo, sería genial tenerlo listo para usar como RavenDB.
Tamir
De manera similar al complemento embedmongo-maven-plugin mencionado aquí, también hay un complemento Gradle Mongo disponible. Al igual que el complemento Maven, también envuelve la api flapdoodle EmbeddedMongoDb y le permite ejecutar una instancia administrada de Mongo desde sus compilaciones de Gradle.
Robert Taylor
Consulte este ejemplo de código aquí: github.com/familysyan/embedded-mongo-integ . Sin instalación, sin dependencia. Es simplemente un script de hormiga independiente de la plataforma que se descarga y configura por usted. También limpia todo después de tus pruebas.
Edmond

Respuestas:

9

Aquí hay una versión actualizada (para 2019) de la respuesta aceptada de @rozky (se ha cambiado mucho en las bibliotecas Mongo y Embedded MongoDB).

package com.example.mongo;

import com.mongodb.BasicDBObject;
import com.mongodb.MongoClient;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoDatabase;
import de.flapdoodle.embed.mongo.MongodExecutable;
import de.flapdoodle.embed.mongo.MongodProcess;
import de.flapdoodle.embed.mongo.MongodStarter;
import de.flapdoodle.embed.mongo.config.IMongodConfig;
import de.flapdoodle.embed.mongo.config.MongodConfigBuilder;
import de.flapdoodle.embed.mongo.config.Net;
import de.flapdoodle.embed.mongo.distribution.Version;
import de.flapdoodle.embed.process.runtime.Network;
import java.util.Date;
import org.junit.After;
import static org.junit.Assert.*;
import org.junit.Before;
import org.junit.Test;

public class EmbeddedMongoTest
{
    private static final String DATABASE_NAME = "embedded";

    private MongodExecutable mongodExe;
    private MongodProcess mongod;
    private MongoClient mongo;

    @Before
    public void beforeEach() throws Exception {
        MongodStarter starter = MongodStarter.getDefaultInstance();
        String bindIp = "localhost";
        int port = 12345;
        IMongodConfig mongodConfig = new MongodConfigBuilder()
        .version(Version.Main.PRODUCTION)
        .net(new Net(bindIp, port, Network.localhostIsIPv6()))
        .build();
        this.mongodExe = starter.prepare(mongodConfig);
        this.mongod = mongodExe.start();
        this.mongo = new MongoClient(bindIp, port);
    }

    @After
    public void afterEach() throws Exception {
        if (this.mongod != null) {
            this.mongod.stop();
            this.mongodExe.stop();
        }
    }

    @Test
    public void shouldCreateNewObjectInEmbeddedMongoDb() {
        // given
        MongoDatabase db = mongo.getDatabase(DATABASE_NAME);
        db.createCollection("testCollection");
        MongoCollection<BasicDBObject> col = db.getCollection("testCollection", BasicDBObject.class);

        // when
        col.insertOne(new BasicDBObject("testDoc", new Date()));

        // then
        assertEquals(1L, col.countDocuments());
    }

}
Collin Krawll
fuente
1
El inicio y la parada repetidos de Mongo incrustado para cada prueba falla en la mayoría de las pruebas. Es mejor comenzar antes de todas las pruebas y apagar una vez que se hayan ejecutado
DBS
Debe incluir @DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS)junto con el cambio anterior
DBS
@DBS También podría usar un puerto aleatorio para poder ejecutar sus pruebas al mismo tiempo en una nueva instancia de mongo incrustada. Vea los documentos aquí .
Collin Krawll
95

Encontré la biblioteca Embedded MongoDB que parece bastante prometedora y hace lo que ha pedido.

Actualmente es compatible con las versiones de MongoDB: 1.6.5a 3.1.6, siempre que los binarios todavía estén disponibles en el espejo configurado.

Aquí hay un breve ejemplo de uso, que acabo de probar y funciona perfectamente:

public class EmbeddedMongoTest {
    private static final String DATABASE_NAME = "embedded";

    private MongodExecutable mongodExe;
    private MongodProcess mongod;
    private Mongo mongo;

    @Before
    public void beforeEach() throws Exception {
        MongoDBRuntime runtime = MongoDBRuntime.getDefaultInstance();
        mongodExe = runtime.prepare(new MongodConfig(Version.V2_3_0, 12345, Network.localhostIsIPv6()));
        mongod = mongodExe.start();
        mongo = new Mongo("localhost", 12345);
    }

    @After
    public void afterEach() throws Exception {
        if (this.mongod != null) {
            this.mongod.stop();
            this.mongodExe.stop();
        }
    }

    @Test
    public void shouldCreateNewObjectInEmbeddedMongoDb() {
        // given
        DB db = mongo.getDB(DATABASE_NAME);
        DBCollection col = db.createCollection("testCollection", new BasicDBObject());

        // when
        col.save(new BasicDBObject("testDoc", new Date()));

        // then
        assertThat(col.getCount(), Matchers.is(1L));
    }
}
rozky
fuente
1
Acabo de usar esta biblioteca y funcionó perfectamente JUnit probando una API de Mongo en una Mac. Recomendado.
Martin Dow
1
+1 excelente hallazgo! Cuando comencé a usar mongodb hace un año, no tener una forma programática de probar contra una base de datos fue una de las desventajas. Solucionamos esto al tener una instancia de prueba en cada entorno, configurada a través de un archivo de propiedades de Java pero, por supuesto, eso necesitaba tener mongo instalado en cada entorno. Parece que esto resolverá todo eso.
andyb
¡Agradable! borré mi respuesta porque ya no es precisa. ¿Alguien tiene idea de lo maduro que es esto? Me imagino que tener que simular MongoDB en un nivel muy bajo sería bastante complicado y, a juzgar por la fuente, parece bastante alto.
Remon van Vliet
Finalmente pude jugar con esto en mi proyecto y puedo informar que fue increíblemente fácil de configurar y ejecutar. Las llamadas de bajo nivel son parte de la com.mongodb API oficial de Java, por lo que no es más complicado que usar la API normal.
andyb
17
Tenga cuidado con esta solución. Simplemente recopila información sobre el sistema operativo actual y descarga los binarios de MongoDB específicos de la plataforma apropiados de Internet, ejecuta el demonio y hace algunas otras cosas de configuración. Como solución empresarial, esto no es así. Burlarse puede ser la única opción real.
James Watkins
18

Hay un producto de Foursquare Fongo . Fongo es una implementación java en memoria de mongo. Intercepta llamadas al controlador estándar mongo-java para buscar, actualizar, insertar, eliminar y otros métodos. El uso principal es para pruebas unitarias ligeras en las que no desea iniciar un proceso mongo.

zlob
fuente
1
¿Fongo intercepta llamadas a la red, por ejemplo, a localhost: 27017 para que pueda servir como un servidor falso para permitir las pruebas de integración sin cambios de código?
mongo-java-server es una implementación de servidor falso que se puede utilizar para pruebas de integración sin cambios de código.
Benedikt Waldvogel
7

Si está utilizando Maven, puede estar interesado en un complemento que he creado que envuelve la API flapdoodle.de 'embedded mongo' :

embedmongo-maven-plugin

Proporciona un startobjetivo que puede utilizar para iniciar cualquier versión de MongoDB que desee (por ejemplo, durante pre-integration-test) y un stopobjetivo que detendrá MongoDB (por ejemplo, durante post-integration-test).

El beneficio real de usar este complemento sobre otros es que no es necesario instalar MongoDB de antemano. Los binarios de MongoDB se descargan y almacenan ~/.embedmongopara futuras compilaciones.

joelittlejohn
fuente
Y aquí está la versión de Clojure para Leiningen: github.com/joelittlejohn/lein-embongo
joelittlejohn
4

con spring-boot 1.3 puede usar EmbeddedMongoAutoConfiguration

pom.xml

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>1.3.2.RELEASE</version>
</parent>
 ...
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-mongodb</artifactId>
    </dependency>
    <dependency>
        <groupId>de.flapdoodle.embed</groupId>
        <artifactId>de.flapdoodle.embed.mongo</artifactId>
        <version>${embedded-mongo.version}</version>
    </dependency>

MongoConfig

@Configuration
@EnableAutoConfiguration(exclude = { EmbeddedMongoAutoConfiguration.class })
public class MongoConfig{
}
panser
fuente
1
¿Puede explicar qué está haciendo realmente la anotación "@EnableAutoConfiguration (exclude = {EmbeddedMongoAutoConfiguration.class})"?
Bruno Negrão Zica
Lo más probable es que la razón sea la dependencia de.flapdoodle.embed.mongo no marcada para el alcance de la prueba. Para no recogerlo y ejecutar mongo integrado en la configuración de la aplicación de producción, se necesita la exclusión.
Sergey Shcherbakov
3

Puede ejecutar MongoDB en memoria a partir de la versión 3.2.6. Desde el sitio :

A partir de la versión 3.2.6 de MongoDB Enterprise, el motor de almacenamiento en memoria es parte de la disponibilidad general (GA) en las compilaciones de 64 bits. Aparte de algunos metadatos y datos de diagnóstico, el motor de almacenamiento en memoria no mantiene ningún dato en el disco, incluidos datos de configuración, índices, credenciales de usuario, etc.

Irwin
fuente
1

No solo para pruebas unitarias, sino que también explicó cómo usar inmemory mongodb con rest api.

dependencia de maven:

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-mongodb</artifactId>
        </dependency>

        <dependency>
            <groupId>de.flapdoodle.embed</groupId>
            <artifactId>de.flapdoodle.embed.mongo</artifactId>
        </dependency>

================================================ ===========================

application.properties

server.port = 8080
spring.data.mongodb.database=user_db
spring.data.mongodb.port=27017
spring.data.mongodb.host=localhost

================================================ ===========================

UserRepository.java

UserRepository de interfaz pública extiende MongoRepository {

}

para referencia y todo el código Java, use el siguiente enlace: (explicación paso a paso)

https://www.youtube.com/watch?v=2Tq2Q7EzhSA&t=7s

Dheeraj kumar
fuente
0

Las actuaciones son mejores cuando se ejecuta mongodconstorageEngine='ephemeralForTest'

new MongodConfigBuilder()
    .version(Version.Main.PRODUCTION)
    .cmdOptions(new MongoCmdOptionsBuilder()
         .useStorageEngine("ephemeralForTest")
         .build())
    .net(new Net("localhost", port, Network.localhostIsIPv6()))
    .build()
burbujas
fuente
-1

En producción, utilizará una base de datos real.

Si desea que sus pruebas reflejen cómo se comporta su producto en producción, use una instancia real de Mongo.

Es posible que una implementación falsa no se comporte exactamente igual que una real. Al realizar la prueba, debe esforzarse por lograr la corrección. La velocidad de ejecución ocupa el segundo lugar.

Jackson
fuente
6
Creo que perdiste mi propósito. No estaba buscando una instancia falsa de Mongo, quería una instancia real pero incrustada en mis pruebas. La razón fue iniciar MongoDB y ponerlo en un estado particular sin contaminar una base de datos existente, ejecutar una serie de operaciones y luego inspeccionar el resultado sin necesidad de examinar datos arbitrarios que no estaban relacionados con mi prueba. Tan real como sea posible mientras se mantiene un entorno de prueba controlado.
seanhodges
Lo siento, la palabra "simular" y todas estas sugerencias "en memoria" me hicieron olvidar el significado de "incrustado" en Java-land. Alegra oírlo.
Jackson