Eingebettete MongoDB beim Ausführen von Integrationstests

111

Meine Frage ist eine Variation davon .

Da mein Java-Web-App-Projekt viele Lesefilter / -abfragen und Schnittstellen zu Tools wie GridFS erfordert, fällt es mir schwer, einen vernünftigen Weg zu finden, um MongoDB so einzusetzen, wie es die obige Lösung vorschlägt.

Daher erwäge ich, neben meinen Integrationstests eine eingebettete Instanz von MongoDB auszuführen. Ich möchte, dass es automatisch gestartet wird (entweder für jeden Test oder für die gesamte Suite), die Datenbank für jeden Test leert und am Ende herunterfährt . Diese Tests können sowohl auf Entwicklungsmaschinen als auch auf dem CI-Server ausgeführt werden, sodass meine Lösung auch portabel sein muss .

Kann mir jemand mit mehr Wissen über MongoDB helfen, eine Vorstellung von der Machbarkeit dieses Ansatzes zu bekommen, und / oder vielleicht Lesematerial vorschlagen, das mir beim Einstieg helfen könnte?

Ich bin auch offen für andere Vorschläge, wie ich dieses Problem angehen könnte ...

Seanhodges
quelle
Wenn Sie maven verwenden, können Sie unser mvnrepository.com/artifact/com.wenzani/mongodb-maven-plugin
markdsievers
Sie können auch dieses Projekt überprüfen, das eine MongoDB im JVM-Speicher simuliert. github.com/thiloplanz/jmockmongo Aber es ist noch in der Entwicklung.
Sebastien Lorber
Nicht nur für Unit-Tests, sondern lesen Sie diesen Blog-Beitrag, wenn Sie MongoDB (sogar einen Cluster) als In-Memory-Bereitstellung ausführen möchten, wenn Sie Linux verwenden. edgystuff.tumblr.com/post/49304254688 Wäre aber großartig, wenn es wie RavenDB aus der Box wäre.
Tamir
Ähnlich wie beim hier erwähnten Embedmongo-Maven-Plugin gibt es auch ein Gradle Mongo Plugin . Wie das Maven-Plugin umschließt es auch die flapdoodle EmbeddedMongoDb-API und ermöglicht es Ihnen, eine verwaltete Instanz von Mongo aus Ihren Gradle-Builds auszuführen.
Robert Taylor
Überprüfen Sie dieses Codebeispiel hier: github.com/familysyan/embedded-mongo-integ . Keine Installation, keine Abhängigkeit. Es ist einfach ein plattformunabhängiges Ameisenskript, das für Sie heruntergeladen und eingerichtet wird. Es räumt auch alles nach Ihren Tests auf.
Edmond

Antworten:

8

Hier ist eine aktualisierte (für 2019) Version der akzeptierten Antwort von @rozky (sowohl in der Mongo-Bibliothek als auch in der eingebetteten MongoDB-Bibliothek wurde viel geändert).

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
quelle
1
Das wiederholte Starten und Stoppen von Embedded Mongo für jeden Test schlägt bei den meisten Tests fehl. Es ist besser, vor allen Tests zu beginnen und herunterzufahren, sobald alle ausgeführt wurden
DBS
Sie müssen @DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS)zusammen mit der obigen Änderung
DBS
@DBS Sie können möglicherweise auch einen zufälligen Port verwenden, damit Sie Ihre Tests weiterhin gleichzeitig auf einer neuen eingebetteten Mongo-Instanz ausführen können. Siehe die Dokumente hier .
Collin Krawll
95

Ich habe die Embedded MongoDB- Bibliothek gefunden, die vielversprechend aussieht und das tut, wonach Sie gefragt haben.

Unterstützt derzeit MongoDB-Versionen: 1.6.5bis 3.1.6, sofern die Binärdateien weiterhin über den konfigurierten Spiegel verfügbar sind.

Hier ist ein kurzes Anwendungsbeispiel, das ich gerade ausprobiert habe und das perfekt funktioniert:

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
quelle
1
Ich habe gerade diese Bibliothek verwendet und es hat perfekt funktioniert. JUnit hat eine Mongo-API auf einem Mac getestet. Empfohlen.
Martin Dow
1
+1 ausgezeichneter Fund! Als ich vor einem Jahr anfing, Mongodb zu verwenden, war es einer der Nachteile, keine programmatische Möglichkeit zum Testen gegen eine Datenbank zu haben. Wir haben dies umgangen, indem wir in jeder Umgebung eine Testinstanz hatten, die über eine Java-Eigenschaftendatei konfiguriert wurde, aber natürlich musste Mongo in jeder Umgebung installiert sein. Das sieht so aus, als würde es all das lösen.
Andyb
Nett! hat meine Antwort gelöscht, da sie nicht mehr korrekt ist. Hat jemand eine Idee, wie ausgereift das ist? Ich kann mir vorstellen, dass es ziemlich kompliziert wäre, MongoDB auf einem sehr niedrigen Level zu simulieren, und nach der Quelle zu urteilen, sieht es ziemlich hoch aus.
Remon van Vliet
Endlich habe ich in meinem Projekt damit gespielt und kann berichten, dass es unglaublich einfach einzurichten und auszuführen war. Die Low-Level-Aufrufe sind alle Teil der offiziellen com.mongodb Java-API, daher ist es nicht komplizierter als die Verwendung der regulären API.
Andyb
16
Seien Sie vorsichtig mit dieser Lösung. Es sammelt nur Informationen über das aktuelle Betriebssystem und lädt die entsprechenden plattformspezifischen MongoDB-Binärdateien aus dem Internet herunter, führt den Dämon aus und führt einige andere Konfigurationsaufgaben aus. Als Unternehmenslösung ist dies nicht der Fall. Spott kann die einzige echte Option sein.
James Watkins
18

Es gibt Foursquare Produkt Fongo . Fongo ist eine In-Memory-Java-Implementierung von Mongo. Es fängt Aufrufe des Standard-Mongo-Java-Treibers ab, um zu finden, zu aktualisieren, einzufügen, zu entfernen und andere Methoden zu verwenden. Die Hauptanwendung ist das Testen von leichten Einheiten, bei denen Sie keinen Mongo-Prozess starten möchten.

zlob
quelle
1
Fängt Fongo zufällig Anrufe an das Netzwerk ab, z. B. an localhost: 27017, damit es als gefälschter Drop-In-Server dienen kann, um Integrationstests ohne Codeänderungen zu ermöglichen?
mongo-java-server ist eine gefälschte Drop-In-Server-Implementierung, die für Integrationstests ohne Codeänderungen verwendet werden kann.
Benedikt Waldvogel
7

Wenn Sie Maven verwenden, könnte Sie ein Plugin interessieren, das ich erstellt habe und das die 'embedded mongo'-API von flapdoodle.de umschließt :

Embedmongo-Maven-Plugin

Es bietet ein startZiel, mit dem Sie eine beliebige Version von MongoDB starten können (z. B. während pre-integration-test), und ein stopZiel, mit dem MongoDB gestoppt wird (z post-integration-test. B. während ).

Der eigentliche Vorteil der Verwendung dieses Plugins gegenüber anderen besteht darin, dass MongoDB nicht im Voraus installiert werden muss. MongoDB-Binärdateien werden heruntergeladen und ~/.embedmongofür zukünftige Builds gespeichert .

joelittlejohn
quelle
Und hier ist die Clojure-Version für Leiningen: github.com/joelittlejohn/lein-embongo
joelittlejohn
4

Mit Spring-Boot 1.3 können Sie EmbeddedMongoAutoConfiguration verwenden

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
quelle
1
Können Sie erklären, was die Annotation "@EnableAutoConfiguration (exclude = {EmbeddedMongoAutoConfiguration.class})" tatsächlich tut?
Bruno Negrão Zica
Der Grund ist höchstwahrscheinlich die Abhängigkeit de.flapdoodle.embed.mongo, die nicht für den Testbereich markiert ist. Um es nicht aufzunehmen und Embedded Mongo im Setup der Produktionsanwendung auszuführen, ist der Ausschluss erforderlich.
Sergey Shcherbakov
3

Sie können MongoDB ab Version 3.2.6 im Speicher ausführen. Von der Website :

Ab MongoDB Enterprise Version 3.2.6 ist die In-Memory-Speicher-Engine Teil der allgemeinen Verfügbarkeit (General Availability, GA) in den 64-Bit-Builds. Abgesehen von einigen Metadaten und Diagnosedaten verwaltet die In-Memory-Speicher-Engine keine Daten auf der Festplatte, einschließlich Konfigurationsdaten, Indizes, Benutzeranmeldeinformationen usw.

Irwin
quelle
0

Nicht nur für Unit-Tests, sondern auch für die Verwendung von Inmemory Mongodb mit Rest-API.

Maven-Abhängigkeit:

        <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

öffentliche Schnittstelle UserRepository erweitert MongoRepository {

}}

Als Referenz und für den gesamten Java-Code verwenden Sie den folgenden Link: (schrittweise Erklärung)

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

Dheeraj Kumar
quelle
0

Die Leistung ist bei der Ausführung mongodmit besserstorageEngine='ephemeralForTest'

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

In der Produktion verwenden Sie eine echte Datenbank.

Wenn Sie möchten, dass Ihre Tests das Verhalten Ihres Produkts in der Produktion widerspiegeln, verwenden Sie eine echte Instanz von Mongo.

Eine gefälschte Implementierung verhält sich möglicherweise nicht genau so wie eine echte. Beim Testen sollten Sie auf Richtigkeit achten. Die Ausführungsgeschwindigkeit steht an zweiter Stelle.

Jackson
quelle
6
Ich denke, du hast meinen Zweck verfehlt. Ich habe nicht nach einer gefälschten Instanz von Mongo gesucht, ich wollte eine echte Instanz, aber eingebettet in meine Tests. Der Grund war, MongoDB zu starten und in einen bestimmten Zustand zu versetzen, ohne eine vorhandene Datenbank zu verschmutzen, eine Reihe von Vorgängen auszuführen und dann das Ergebnis zu überprüfen, ohne beliebige Daten durchsuchen zu müssen, die nicht mit meinem Test zusammenhängen. So real wie es nur sein kann, während eine kontrollierte Testumgebung beibehalten wird.
Seanhodges
Entschuldigung, das Wort "simulieren" und all diese "In-Memory" -Vorschläge haben mich veranlasst, die Bedeutung von "eingebettet" in Java-Land zu vergessen. Froh das zu hören.
Jackson