Osadzone MongoDB podczas uruchamiania testów integracji

112

Moje pytanie jest odmianą tego .

Ponieważ mój projekt aplikacji internetowej Java wymaga wielu filtrów / zapytań odczytu i interfejsów z narzędziami takimi jak GridFS, staram się wymyślić rozsądny sposób wykorzystania MongoDB w sposób sugerowany przez powyższe rozwiązanie.

Dlatego rozważam uruchomienie osadzonej instancji MongoDB wraz z moimi testami integracji. Chciałbym, aby uruchamiał się automatycznie (dla każdego testu lub całego zestawu), opróżniał bazę danych dla każdego testu i wyłączał się na końcu. Te testy mogą być uruchamiane na maszynach programistycznych, a także na serwerze CI, więc moje rozwiązanie również musi być przenośne .

Czy ktoś, kto ma większą wiedzę na temat MongoDB, może mi pomóc zorientować się w wykonalności tego podejścia i / lub może zasugerować materiały do ​​czytania, które mogą mi pomóc w rozpoczęciu?

Jestem również otwarty na inne sugestie, które ludzie mogą mieć, jak rozwiązać ten problem ...

seanhodges
źródło
Jeśli używasz mavena, możesz użyć naszego mvnrepository.com/artifact/com.wenzani/mongodb-maven-plugin
markdsievers
Możesz również sprawdzić ten projekt, który symuluje MongoDB w pamięci JVM. github.com/thiloplanz/jmockmongo Ale wciąż jest w fazie rozwoju.
Sebastien Lorber,
Nie [tylko] do testów jednostkowych, ale przeczytaj ten wpis na blogu, jeśli chcesz uruchomić MongoDB (nawet klaster) jako wdrożenie w pamięci, jeśli używasz Linuksa. edgystuff.tumblr.com/post/49304254688 Byłoby wspaniale mieć go po wyjęciu z pudełka, tak jak RavenDB.
Tamir
Podobnie jak wspomniana tutaj wtyczka embedmongo-maven-plugin, dostępna jest również wtyczka Gradle Mongo . Podobnie jak wtyczka Maven, otacza również interfejs API flapdoodle EmbeddedMongoDb i umożliwia uruchamianie zarządzanej instancji Mongo z kompilacji Gradle.
Robert Taylor
Sprawdź ten przykład kodu tutaj: github.com/familysyan/embedded-mongo-integ . Bez instalacji, bez zależności. Jest to po prostu niezależny od platformy skrypt Ant, który pobiera i konfiguruje za Ciebie. Czyści również wszystko po twoich testach.
Edmond,

Odpowiedzi:

9

Oto zaktualizowana (na rok 2019) wersja zaakceptowanej odpowiedzi od @rozky (wiele się zmieniło w bibliotekach Mongo i 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
źródło
1
Powtarzane uruchamianie i zatrzymywanie Embedded mongo dla każdego testu kończy się niepowodzeniem większości testów. Lepiej zacząć przed wszystkimi testami i zamknięciem, gdy wszystkie zostaną wykonane
DBS,
Musisz dołączyć @DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS)do powyższej zmiany
DBS
@DBS Możesz także użyć losowego portu, aby móc jednocześnie uruchamiać testy na nowej, osadzonej instancji mongo. Zobacz dokumentację tutaj .
Collin Krawll
95

Znalazłem wbudowaną bazę danych MongoDB bibliotekę która wygląda całkiem obiecująco i robi to, o co prosiłeś.

Obecnie obsługuje wersje MongoDB: 1.6.5do3.1.6 , pod warunkiem, że pliki binarne są nadal dostępne ze skonfigurowanego serwera lustrzanego.

Oto krótki przykład użycia, który właśnie wypróbowałem i działa idealnie:

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
źródło
1
Właśnie użyłem tej biblioteki i działało doskonale JUnit testując API Mongo na komputerze Mac. Zalecana.
Martin Dow,
1
+1 doskonałe znalezisko! Kiedy rok temu po raz pierwszy zacząłem używać mongodb, brak możliwości programowego testowania bazy danych był jedną z wad. Poradziliśmy sobie z tym, mając instancję testową w każdym środowisku, skonfigurowaną za pomocą pliku właściwości Java, ale oczywiście wymagało to zainstalowania mongo w każdym środowisku. Wygląda na to, że rozwiąże to wszystko.
andyb
Miły! usunąłem moją odpowiedź, ponieważ nie jest już dokładna. Czy ktoś ma pojęcie, jak dojrzałe to jest? Mogę sobie wyobrazić, że symulacja MongoDB na bardzo niskim poziomie byłaby dość skomplikowana i sądząc po źródle, wygląda na dość wysoki poziom.
Remon van Vliet
Wreszcie mogłem się tym bawić w moim projekcie i mogę zgłosić, że jest niesamowicie łatwy w konfiguracji i uruchomieniu. Wszystkie wywołania niskiego poziomu są częścią oficjalnego com.mongodb interfejsu API języka Java, więc nie jest to bardziej skomplikowane niż użycie zwykłego interfejsu API.
andyb
17
Uważaj na to rozwiązanie. Po prostu zbiera informacje o aktualnym systemie operacyjnym i pobiera z Internetu odpowiednie pliki binarne MongoDB specyficzne dla platformy, uruchamia demona i wykonuje inne czynności konfiguracyjne. Nie jest to rozwiązanie dla przedsiębiorstw. Kpiny mogą być jedyną realną opcją.
James Watkins,
18

Jest produkt Foursquare Fongo . Fongo to implementacja mongo w języku Java w pamięci. Przechwytuje wywołania standardowego sterownika mongo-java w celu znalezienia, aktualizacji, wstawienia, usunięcia i innych metod. Głównym zastosowaniem jest lekkie testowanie jednostkowe, w którym nie chcesz przyspieszać procesu mongo.

zlob
źródło
1
Czy zdarza się, że Fongo przechwytuje wywołania do sieci, np. Do localhost: 27017, aby mógł służyć jako fałszywy serwer, umożliwiający testowanie integracji bez zmian w kodzie?
mongo-java-server to fałszywa implementacja serwera typu drop-in, której można używać do testowania integracji bez zmian w kodzie.
Benedikt Waldvogel
7

Jeśli używasz Mavena, możesz być zainteresowany wtyczką, którą stworzyłem, która otacza API 'embedded mongo' flapdoodle.de :

embedmongo-maven-plugin

Zapewnia startcel, którego możesz użyć do uruchomienia dowolnej wersji MongoDB (np. W trakcie pre-integration-test) oraz stopcel, który zatrzyma MongoDB (np. W trakcie post-integration-test).

Prawdziwą zaletą korzystania z tej wtyczki w porównaniu z innymi jest to, że nie ma potrzeby wcześniejszej instalacji MongoDB. Pliki binarne MongoDB są pobierane i przechowywane na ~/.embedmongopotrzeby przyszłych kompilacji.

joelittlejohn
źródło
A oto wersja Clojure dla Leiningen: github.com/joelittlejohn/lein-embongo
joelittlejohn
4

dzięki spring-boot 1.3 możesz użyć 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
źródło
1
czy możesz wyjaśnić, co właściwie robi adnotacja „@EnableAutoConfiguration (exclude = {EmbeddedMongoAutoConfiguration.class})”?
Bruno Negrão Zica
Przyczyną jest najprawdopodobniej zależność de.flapdoodle.embed.mongo nie zaznaczona jako zakres testowy. Aby nie podnieść go i nie uruchomić osadzonego mongo w konfiguracji aplikacji produkcyjnej, konieczne jest wykluczenie.
Sergey Shcherbakov
3

Możesz uruchomić MongoDB w pamięci od wersji 3.2.6. Ze strony :

Począwszy od MongoDB Enterprise w wersji 3.2.6, aparat magazynowania w pamięci jest częścią ogólnej dostępności (GA) w kompilacjach 64-bitowych. Poza niektórymi metadanymi i danymi diagnostycznymi, silnik przechowywania w pamięci nie przechowuje żadnych danych na dysku, w tym danych konfiguracyjnych, indeksów, poświadczeń użytkownika itp.

Irwin
źródło
1

Nie tylko do testowania jednostkowego, ale także wyjaśnił, jak używać inmemory mongodb z resztą api.

zależność 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

publiczny interfejs UserRepository rozszerza MongoRepository {

}

w celach informacyjnych, a cały kod java użyj poniższego linku: (wyjaśnienie krok po kroku)

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

Dheeraj kumar
źródło
0

Wydajność jest lepsza podczas wykonywania mongodzstorageEngine='ephemeralForTest'

new MongodConfigBuilder()
    .version(Version.Main.PRODUCTION)
    .cmdOptions(new MongoCmdOptionsBuilder()
         .useStorageEngine("ephemeralForTest")
         .build())
    .net(new Net("localhost", port, Network.localhostIsIPv6()))
    .build()
bąbelki
źródło
-1

W produkcji będziesz korzystać z prawdziwej bazy danych.

Jeśli chcesz, aby Twoje testy odzwierciedlały zachowanie Twojego produktu podczas produkcji, użyj prawdziwej instancji Mongo.

Fałszywa implementacja może nie zachowywać się dokładnie tak samo, jak prawdziwa. Podczas testowania powinieneś dążyć do poprawności. Szybkość wykonania jest na drugim miejscu.

Jackson
źródło
6
Myślę, że przegapiłeś mój cel. Nie szukałem fałszywej instancji Mongo, chciałem prawdziwej instancji, ale osadzonej w moich testach. Powodem było uruchomienie MongoDB i wprowadzenie go w konkretny stan bez zanieczyszczania istniejącej bazy danych, wykonanie serii operacji, a następnie sprawdzenie wyniku bez konieczności przeszukiwania dowolnych danych, które nie były związane z moim testem. Tak realne, jak to tylko możliwe, przy jednoczesnym utrzymaniu kontrolowanego środowiska testowego.
seanhodges
Przepraszam, słowo „symulować” i wszystkie te sugestie „w pamięci” sprawiły, że zapomniałem o znaczeniu słowa „osadzony” w środowisku Java. Miło to słyszeć.
Jackson