MongoDB incorporato durante l'esecuzione dei test di integrazione


112

La mia domanda è una variazione di questa .

Poiché il mio progetto di app Web Java richiede molti filtri / query di lettura e interfacce con strumenti come GridFS, sto lottando per pensare a un modo sensato per utilizzare MongoDB nel modo suggerito dalla soluzione sopra.

Pertanto, sto considerando di eseguire un'istanza incorporata di MongoDB insieme ai miei test di integrazione. Mi piacerebbe che si avviasse automaticamente (sia per ogni test che per l'intera suite), svuota il database per ogni test e si spenga alla fine. Questi test potrebbero essere eseguiti su macchine di sviluppo oltre che sul server CI, quindi anche la mia soluzione dovrà essere portabile .

Qualcuno con maggiori conoscenze su MongoDB può aiutarmi a farmi un'idea della fattibilità di questo approccio e / o forse suggerire qualsiasi materiale di lettura che possa aiutarmi a iniziare?

Sono anche aperto ad altri suggerimenti che le persone potrebbero avere su come affrontare questo problema ...



Puoi anche controllare questo progetto che simula un MongoDB all'interno della memoria JVM. github.com/thiloplanz/jmockmongo Ma è ancora in fase di sviluppo.
Sebastien Lorber

Non [solo per] per gli unit test, ma leggi questo post del blog se ti piace eseguire MongoDB (anche un cluster) come distribuzione in memoria se stai usando Linux. edgystuff.tumblr.com/post/49304254688 Sarebbe bello averlo fuori dagli schemi come RavenDB però.
Tamir

Simile al plug-in embedmongo-maven menzionato qui, è disponibile anche un plug-in Gradle Mongo . Come il plug-in Maven, include anche l' api flapdoodle EmbeddedMongoDb e consente di eseguire un'istanza gestita di Mongo dalle build di Gradle.
Robert Taylor

Controlla questo esempio di codice qui: github.com/familysyan/embedded-mongo-integ . Nessuna installazione, nessuna dipendenza. È semplicemente uno script di formiche indipendente dalla piattaforma che viene scaricato e configurato per te. Pulisce anche tutto dopo i test.
Edmond

Risposte:


9

Ecco una versione aggiornata (per il 2019) della risposta accettata da @rozky (molto è stato cambiato sia nelle librerie Mongo che in quelle 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());
    }

}

1
L'avvio e l'arresto ripetuti di Embedded mongo per ogni test falliscono la maggior parte dei test. È meglio iniziare prima di tutti i test e spegnerlo una volta che tutti sono stati eseguiti
DBS

È necessario includere @DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS)insieme alla modifica di cui sopra
DBS

@DBS Potresti anche usare una porta casuale in modo da poter ancora eseguire i tuoi test contemporaneamente su una nuova istanza di mongo incorporata. Vedi i documenti qui .
Collin Krawll

95

Ho trovato la libreria MongoDB incorporata che sembra piuttosto promettente e fa quello che hai chiesto.

Attualmente supporta le versioni di MongoDB: 1.6.5a 3.1.6, a condizione che i binari siano ancora disponibili dal mirror configurato.

Ecco un breve esempio di utilizzo, che ho appena provato e funziona perfettamente:

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));
    }
}

1
Ho appena usato questa libreria e ha funzionato perfettamente JUnit testando un'API Mongo su un Mac. Consigliato.
Martin Dow

1
+1 scoperta eccellente! Quando ho iniziato a usare mongodb un anno fa, non avere un modo programmatico per testare un database era uno degli svantaggi. Abbiamo aggirato questo problema avendo un'istanza di test su ogni ambiente, configurata tramite un file delle proprietà Java, ma ovviamente era necessario che mongo fosse installato su ogni ambiente. Sembra che risolverà tutto ciò.
andyb

Bello! ha cancellato la mia risposta poiché non è più accurata. Qualcuno ha idea di quanto sia maturo questo? Posso immaginare che dover simulare MongoDB a un livello molto basso sarebbe piuttosto complicato e, a giudicare dalla fonte, sembra piuttosto di alto livello.
Remon van Vliet,

Finalmente ho potuto giocare con questo nel mio progetto e posso riferire che è stato incredibilmente facile da configurare ed eseguire. Le chiamate di basso livello fanno tutte parte com.mongodb dell'API Java ufficiale, quindi non è più complicato dell'utilizzo dell'API regolare.
andyb

17
Stai attento con questa soluzione. Raccoglie solo informazioni sul sistema operativo corrente e scarica i binari MongoDB specifici della piattaforma da Internet, esegue il demone e fa altre cose di configurazione. Come soluzione aziendale, non lo è. La derisione può essere l'unica vera opzione.
James Watkins,

18

C'è il prodotto Foursquare Fongo . Fongo è un'implementazione java in memoria di mongo. Intercetta le chiamate al driver mongo-java standard per trovare, aggiornare, inserire, rimuovere e altri metodi. L'uso principale è per test unitari leggeri in cui non si desidera avviare un processo mongo.


1
Fongo intercetta le chiamate alla rete, ad esempio a localhost: 27017 in modo che possa fungere da falso server drop-in per abilitare il test di integrazione senza modifiche al codice?

mongo-java-server è un'implementazione di server falsi drop-in che può essere utilizzata per test di integrazione senza modifiche al codice.
Benedikt Waldvogel

7

Se stai usando Maven potresti essere interessato a un plugin che ho creato che racchiude l'API 'embedded mongo' flapdoodle.de :

embedmongo-maven-plugin

Fornisce un startobiettivo che puoi utilizzare per avviare qualsiasi versione di MongoDB che desideri (ad esempio durante pre-integration-test) e un stopobiettivo che interromperà MongoDB (ad esempio durante post-integration-test).

Il vero vantaggio dell'utilizzo di questo plug-in rispetto ad altri è che non è necessario installare in anticipo MongoDB. I binari di MongoDB vengono scaricati e archiviati ~/.embedmongoper build future.


Ed ecco la versione Clojure per Leiningen: github.com/joelittlejohn/lein-embongo
joelittlejohn


4

con lo spring-boot 1.3 puoi usare 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{
}

1
puoi spiegare cosa fa effettivamente l'annotazione "@EnableAutoConfiguration (exclude = {EmbeddedMongoAutoConfiguration.class})"?
Bruno Negrão Zica

Il motivo è molto probabilmente la dipendenza de.flapdoodle.embed.mongo non contrassegnata per l'ambito del test. Per non prenderlo in mano ed eseguire mongo incorporato nella configurazione dell'applicazione di produzione, è necessaria l'esclusione.
Sergey Shcherbakov

3

È possibile eseguire MongoDB in memoria a partire dalla versione 3.2.6. Dal sito :

A partire da MongoDB Enterprise versione 3.2.6, il motore di archiviazione in memoria fa parte della disponibilità generale (GA) nelle build a 64 bit. Oltre ad alcuni metadati e dati diagnostici, il motore di archiviazione in memoria non conserva alcun dato su disco, inclusi dati di configurazione, indici, credenziali utente, ecc.


1

Non solo per i test di unità, ma ha anche spiegato come utilizzare inmemory mongodb con rest api.

dipendenza da 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

l'interfaccia pubblica UserRepository estende MongoRepository {

}

per riferimento e tutto il codice java usa il link sottostante: (spiegazione passo passo)

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


0

Le prestazioni sono migliori quando si esegue mongodconstorageEngine='ephemeralForTest'

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

-1

In produzione, utilizzerai un database reale.

Se vuoi che i tuoi test riflettano il comportamento del tuo prodotto in produzione, usa un'istanza reale di Mongo.

Un'implementazione falsa potrebbe non comportarsi esattamente come una vera. Durante il test, dovresti cercare di essere corretto. La velocità di esecuzione è seconda.


6
Penso che ti sia mancato il mio scopo. Non stavo cercando un'istanza falsa di Mongo, volevo un'istanza reale ma incorporata nei miei test. Il motivo era avviare MongoDB e metterlo in uno stato particolare senza inquinare un database esistente, eseguire una serie di operazioni e quindi ispezionare il risultato senza dover setacciare dati arbitrari che non erano correlati al mio test. Per quanto reale possa essere pur mantenendo un ambiente di test controllato.
seanhodges

Spiacenti, la parola "simulare" e tutti questi suggerimenti "in memoria" mi hanno fatto dimenticare il significato di "incorporato" in Java-land. Felice di sentirlo.
Jackson il
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.