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 ...

Seanhodges
fonte
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());
    }

}
Collin Krawll
fonte
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));
    }
}
Rozky
fonte
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.

zlob
fonte
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.

joelittlejohn
fonte
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{
}
panser
fonte
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.

Irwin
fonte
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

Dheeraj Kumar
fonte
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()
bolhas
fonte
-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.

Jackson
fonte
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