MongoDB integrado ao executar testes de integração


112

Minha pergunta é uma variação desta .

Visto que meu projeto de aplicativo Web Java requer muitos filtros / consultas de leitura e interfaces com ferramentas como GridFS, estou lutando para pensar em uma maneira sensata de empregar MongoDB da maneira que a solução acima sugere.

Portanto, estou pensando em executar uma instância incorporada do MongoDB junto com meus testes de integração. Eu gostaria que ele inicializasse automaticamente (para cada teste ou para todo o pacote), esvazie o banco de dados para cada teste e desligue no final. Esses testes podem ser executados em máquinas de desenvolvimento, bem como no servidor de CI, portanto, minha solução também precisará ser portátil .

Alguém com mais conhecimento no MongoDB pode me ajudar a ter uma ideia da viabilidade dessa abordagem e / ou talvez sugerir algum material de leitura que possa me ajudar a começar?

Também estou aberto a outras sugestões que as pessoas possam ter sobre como eu poderia abordar este problema ...


Se você estiver usando o maven, pode usar o nosso mvnrepository.com/artifact/com.wenzani/mongodb-maven-plugin
markdsievers

Você também pode verificar este projeto que simula um MongoDB dentro da memória JVM. github.com/thiloplanz/jmockmongo Mas ainda está em desenvolvimento.
Sebastien Lorber

Não [apenas] para testes de unidade, mas leia esta postagem do blog se você gosta de executar o MongoDB (mesmo um cluster) como implantação na memória se estiver usando Linux. edgystuff.tumblr.com/post/49304254688 Seria ótimo tê-lo pronto como o RavenDB.
Tamir

Semelhante ao plug-in embedmongo-maven mencionado aqui, também há um plug - in do Gradle Mongo disponível. Como o plug-in Maven, ele também envolve o flapdoodle EmbeddedMongoDb api e permite que você execute uma instância gerenciada do Mongo a partir de suas compilações do Gradle.
Robert Taylor

Verifique este exemplo de código aqui: github.com/familysyan/embedded-mongo-integ . Sem instalação, sem dependência. É simplesmente um script de formiga independente de plataforma que faz o download e a configuração para você. Ele também limpa tudo após seus testes.
Edmond

Respostas:


9

Aqui está uma versão atualizada (para 2019) da resposta aceita de @rozky (muitas mudanças foram alteradas nas bibliotecas Mongo e 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
O início e a parada repetidos do mongo incorporado para cada teste falha na maioria dos testes. É melhor começar antes de todos os testes e desligar quando todos forem executados
DBS

Você precisa incluir @DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS)junto com a alteração acima
DBS

@DBS Você também pode usar uma porta aleatória para que ainda possa executar seus testes simultaneamente em uma nova instância integrada do mongo. Veja a documentação aqui .
Collin Krawll

95

Eu encontrei a biblioteca Embedded MongoDB que parece bastante promissora e faz o que você pediu.

Atualmente suporta versões do MongoDB: 1.6.5to 3.1.6, desde que os binários ainda estejam disponíveis no espelho configurado.

Aqui está um pequeno exemplo de uso, que acabei de experimentar e funciona perfeitamente:

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
Acabei de usar essa biblioteca e funcionou perfeitamente JUnit testando uma API Mongo em um Mac. Recomendado.
Martin Dow

1
+1 excelente achado! Quando comecei a usar o mongodb há um ano, não ter uma maneira programática de testar um banco de dados era uma das desvantagens. Conseguimos contornar isso tendo uma instância de teste em cada ambiente, configurada por meio de um arquivo de propriedades Java, mas é claro que precisava ter o mongo instalado em cada ambiente. Parece que vai resolver tudo isso.
andyb de

Agradável! excluiu minha resposta, pois ela não é mais precisa. Alguém tem ideia de como isso é maduro? Posso imaginar que simular o MongoDB em um nível muito baixo seria bastante complicado e, a julgar pela fonte, parece um nível bem alto.
Remon van Vliet

Finalmente comecei a brincar com isso em meu projeto e posso relatar que foi incrivelmente fácil de configurar e executar. As chamadas de baixo nível são todas parte da com.mongodb API Java oficial, portanto, não é mais complicado do que usar a API regular.
andyb

17
Cuidado com esta solução. Ele apenas coleta informações sobre o sistema operacional atual e baixa os binários do MongoDB específicos da plataforma apropriados da Internet, executa o daemon e faz algumas outras configurações. Como uma solução corporativa, isso não é. Zombar pode ser a única opção real.
James Watkins

18

Existe o produto Foursquare Fongo . Fongo é uma implementação java em memória do mongo. Ele intercepta chamadas para o driver mongo-java-padrão para encontrar, atualizar, inserir, remover e outros métodos. O uso principal é para testes de unidade leves, nos quais você não deseja ativar um processo mongo.


1
O Fongo intercepta chamadas para a rede, por exemplo, para localhost: 27017 para que possa servir como um servidor falso para permitir o teste de integração sem alterações de código?

mongo-java-server é uma implementação de servidor falsa drop-in que pode ser usada para teste de integração sem alterações de código.
Benedikt Waldvogel

7

Se você estiver usando o Maven, pode estar interessado em um plug-in que criei que envolve a API 'mongo incorporado' flapdoodle.de :

plugin-embedmongo-maven

Ele fornece uma startmeta que você pode usar para iniciar qualquer versão do MongoDB que desejar (por exemplo, durante pre-integration-test) e uma stopmeta que irá parar o MongoDB (por exemplo, durante post-integration-test).

O verdadeiro benefício de usar este plugin em relação a outros é que não há nenhum requisito para o MongoDB ser instalado de antemão. Os binários do MongoDB são baixados e armazenados ~/.embedmongopara compilações futuras.


E aqui está a versão Clojure para Leiningen: github.com/joelittlejohn/lein-embongo
joelittlejohn


4

com spring-boot 1.3 você pode 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{
}

1
você pode explicar o que a anotação "@EnableAutoConfiguration (exclude = {EmbeddedMongoAutoConfiguration.class})" está realmente fazendo?
Bruno Negrão Zica

O motivo é provavelmente a dependência de.flapdoodle.embed.mongo não marcada para o escopo do teste. Para não pegá-lo e executar o mongo integrado na configuração do aplicativo de produção, a exclusão é necessária.
Sergey Shcherbakov

3

Você pode executar o MongoDB na memória a partir da versão 3.2.6. Do site :

A partir do MongoDB Enterprise versão 3.2.6, o mecanismo de armazenamento em memória faz parte da disponibilidade geral (GA) nas compilações de 64 bits. Além de alguns metadados e dados de diagnóstico, o mecanismo de armazenamento em memória não mantém nenhum dado em disco, incluindo dados de configuração, índices, credenciais de usuário, etc.


1

Não apenas para teste de unidade, mas também explicou como usar o mongodb de memória com api resto.

dependência 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 pública UserRepository extends MongoRepository {

}

para referência e todos os códigos java, use o link abaixo: (explicação passo a passo)

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


0

Os desempenhos são melhores ao executar mongodcomstorageEngine='ephemeralForTest'

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

-1

Na produção, você usará um banco de dados real.

Se você deseja que seus testes reflitam como seu produto se comporta na produção, use uma instância real do Mongo.

Uma implementação falsa pode não se comportar exatamente da mesma forma que uma real. Ao testar, você deve se esforçar para estar correto. A velocidade de execução vem em segundo lugar.


6
Eu acho que você perdeu meu propósito. Eu não estava procurando por uma instância falsa do Mongo, queria uma instância real, mas incorporada aos meus testes. O motivo foi iniciar o MongoDB e colocá-lo em um estado específico sem poluir um banco de dados existente, executar uma série de operações e, em seguida, inspecionar o resultado sem precisar vasculhar dados arbitrários não relacionados ao meu teste. Tão real quanto possível, embora ainda mantendo um ambiente de teste controlado.
seanhodges de

Desculpe, a palavra "simular" e todas essas sugestões "in-memory" me fizeram esquecer o significado de "embutido" no Java-land. Fico feliz em ouvir isso.
Jackson
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.