MongoDB intégré lors de l'exécution des tests d'intégration

112

Ma question est une variante de celle-ci .

Étant donné que mon projet d'application Web Java nécessite beaucoup de filtres / requêtes de lecture et d'interfaces avec des outils tels que GridFS, j'ai du mal à trouver un moyen judicieux d'utiliser MongoDB de la manière suggérée par la solution ci-dessus.

Par conséquent, j'envisage d'exécuter une instance intégrée de MongoDB parallèlement à mes tests d'intégration. J'aimerais qu'il démarre automatiquement (soit pour chaque test, soit pour toute la suite), vider la base de données pour chaque test et s'arrêter à la fin. Ces tests peuvent être exécutés sur des machines de développement ainsi que sur le serveur CI, donc ma solution devra également être portable .

Quelqu'un avec plus de connaissances sur MongoDB peut-il m'aider à avoir une idée de la faisabilité de cette approche, et / ou peut-être suggérer un matériel de lecture qui pourrait m'aider à démarrer?

Je suis également ouvert à d'autres suggestions que les gens pourraient avoir sur la façon dont je pourrais aborder ce problème ...

seanhodges
la source
Si vous utilisez maven, vous pouvez utiliser le nôtre mvnrepository.com/artifact/com.wenzani/mongodb-maven-plugin
markdsievers
Vous pouvez également vérifier ce projet qui simule un MongoDB dans la mémoire JVM. github.com/thiloplanz/jmockmongo Mais il est encore en développement.
Sebastien Lorber
Pas [juste pour] pour les tests unitaires, mais lisez ce billet de blog si vous aimez exécuter MongoDB (même un cluster) en tant que déploiement en mémoire si vous utilisez Linux. edgystuff.tumblr.com/post/49304254688 Ce serait bien de l'avoir prêt à l'emploi comme RavenDB.
Tamir
Semblable au plugin embedmongo-maven-plugin mentionné ici, il existe également un plugin Gradle Mongo disponible. Comme le plugin Maven, il enveloppe également l' api flapdoodle EmbeddedMongoDb et vous permet d'exécuter une instance gérée de Mongo à partir de vos builds Gradle.
Robert Taylor
Consultez cet exemple de code ici: github.com/familysyan/embedded-mongo-integ . Aucune installation, aucune dépendance. C'est simplement un script de fourmi indépendant de la plate-forme qui télécharge et configure pour vous. Il nettoie également tout après vos tests.
Edmond

Réponses:

9

Voici une version mise à jour (pour 2019) de la réponse acceptée de @rozky (beaucoup de choses ont été modifiées dans les bibliothèques Mongo et 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
la source
1
Le démarrage et l'arrêt répétés de mongo intégré pour chaque test échouent la plupart des tests. Il vaut mieux commencer avant tous les tests et arrêter une fois tous exécutés
DBS
Vous devez inclure @DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS)avec le changement ci
DBS
@DBS Vous pouvez peut-être aussi utiliser un port aléatoire afin de pouvoir toujours exécuter vos tests simultanément sur une nouvelle instance mongo intégrée. Consultez la documentation ici .
Collin Krawll
95

J'ai trouvé la bibliothèque Embedded MongoDB qui semble assez prometteuse et fait ce que vous avez demandé.

Prend actuellement en charge les versions MongoDB: 1.6.5to 3.1.6, à condition que les binaires soient toujours disponibles à partir du miroir configuré.

Voici un petit exemple d'utilisation, que je viens d'essayer et qui fonctionne parfaitement:

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
la source
1
Je viens d'utiliser cette bibliothèque et cela a parfaitement fonctionné JUnit testant une API Mongo sur un Mac. Conseillé.
Martin Dow
1
+1 excellente trouvaille! Lorsque j'ai commencé à utiliser mongodb il y a un an, ne pas avoir de méthode programmatique pour tester une base de données était l'un des inconvénients. Nous avons contourné cela en ayant une instance de test sur chaque environnement, configurée via un fichier de propriétés Java, mais bien sûr, il fallait que mongo soit installé sur chaque environnement. On dirait que cela résoudra tout cela.
andyb
Agréable! supprimé ma réponse car elle n'est plus exacte. Quelqu'un a-t-il une idée de sa maturité? Je peux imaginer que devoir simuler MongoDB à un niveau très bas serait assez compliqué et à en juger par la source, cela semble assez élevé.
Remon van Vliet
J'ai enfin pu jouer avec cela dans mon projet et je peux signaler que c'était incroyablement facile à configurer et à exécuter. Les appels de bas niveau font tous partie de l' com.mongodb API Java officielle , ce n'est donc pas plus compliqué que d'utiliser l'API standard.
andyb
17
Soyez prudent avec cette solution. Il rassemble simplement des informations sur le système d'exploitation actuel et télécharge les binaires MongoDB spécifiques à la plate-forme appropriés à partir d'Internet, exécute le démon et effectue d'autres tâches de configuration. En tant que solution d'entreprise, ce n'est pas le cas. La moquerie peut être la seule vraie option.
James Watkins
18

Il existe un produit Foursquare Fongo . Fongo est une implémentation java en mémoire de mongo. Il intercepte les appels au pilote standard mongo-java pour les recherches, les mises à jour, les inserts, les suppressions et d'autres méthodes. L'utilisation principale est pour les tests unitaires légers où vous ne voulez pas lancer un processus mongo.

zlob
la source
1
Fongo intercepte-t-il les appels vers le réseau, par exemple vers localhost: 27017 afin qu'il puisse servir de faux serveur pour permettre des tests d'intégration sans changement de code?
mongo-java-server est une fausse implémentation de serveur qui peut être utilisée pour les tests d'intégration sans changement de code.
Benedikt Waldvogel
7

Si vous utilisez Maven, vous serez peut-être intéressé par un plugin que j'ai créé qui encapsule l' API `` mongo embarquée '' de flapdoodle.de :

embedmongo-maven-plugin

Il fournit un startobjectif que vous pouvez utiliser pour démarrer n'importe quelle version de MongoDB que vous voulez (par exemple pendant pre-integration-test), et un stopobjectif qui arrêtera MongoDB (par exemple pendantpost-integration-test ).

Le véritable avantage de l'utilisation de ce plugin par rapport aux autres est qu'il n'est pas nécessaire d'installer MongoDB au préalable. Les binaires MongoDB sont téléchargés et stockés ~/.embedmongopour les futures versions.

joelittlejohn
la source
Et voici la version Clojure pour Leiningen: github.com/joelittlejohn/lein-embongo
joelittlejohn
4

avec spring-boot 1.3, vous pouvez utiliser 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
la source
1
pouvez-vous expliquer ce que fait réellement l'annotation "@EnableAutoConfiguration (exclude = {EmbeddedMongoAutoConfiguration.class})"?
Bruno Negrão Zica
La raison est très probablement la dépendance de.flapdoodle.embed.mongo non marquée pour la portée du test. Pour ne pas le récupérer et exécuter mongo intégré dans la configuration de l'application de production, l'exclusion est nécessaire.
Sergey Shcherbakov
3

Vous pouvez exécuter MongoDB en mémoire à partir de la version 3.2.6. Depuis le site :

À partir de MongoDB Enterprise version 3.2.6, le moteur de stockage en mémoire fait partie de la disponibilité générale (GA) dans les versions 64 bits. Hormis certaines métadonnées et données de diagnostic, le moteur de stockage en mémoire ne conserve aucune donnée sur disque, y compris les données de configuration, les index, les informations d'identification de l'utilisateur, etc.

Irwin
la source
1

Pas seulement pour les tests unitaires, mais a également expliqué comment utiliser mongodb en mémoire avec l'api de repos.

dépendance 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

interface publique UserRepository étend MongoRepository {

}

pour référence et tout le code java, utilisez le lien ci-dessous: (explication étape par étape)

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

Dheeraj kumar
la source
0

Les performances sont meilleures lors de l'exécution mongodavecstorageEngine='ephemeralForTest'

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

En production, vous utiliserez une vraie base de données.

Si vous souhaitez que vos tests reflètent le comportement de votre produit en production, utilisez une instance réelle de Mongo.

Une fausse implémentation peut ne pas se comporter exactement comme une vraie. Lors du test, vous devez vous efforcer d'obtenir l'exactitude. La vitesse d'exécution vient en second.

Jackson
la source
6
Je pense que tu as manqué mon but. Je ne cherchais pas une fausse instance de Mongo, je voulais une instance réelle mais intégrée dans mes tests. La raison était de démarrer MongoDB et de le mettre dans un état particulier sans polluer une base de données existante, d'exécuter une série d'opérations, puis d'inspecter le résultat sans avoir besoin de passer au crible des données arbitraires qui n'étaient pas liées à mon test. Aussi réel que cela puisse être tout en maintenant un environnement de test contrôlé.
seanhodges le
Désolé, le mot «simuler» et toutes ces suggestions «en mémoire» m'ont fait oublier le sens de «intégré» dans Java-land. Heureux de l'entendre.
Jackson