Встроенный MongoDB при запуске интеграционных тестов


112

Мой вопрос - это вариант этого .

Поскольку мой проект веб-приложения Java требует множества фильтров / запросов чтения и интерфейсов с такими инструментами, как GridFS, я изо всех сил пытаюсь придумать разумный способ использования MongoDB так, как предлагает это решение.

Поэтому я рассматриваю возможность запуска встроенного экземпляра MongoDB вместе с моими интеграционными тестами. Я бы хотел, чтобы он запускался автоматически (либо для каждого теста, либо для всего набора), очищал базу данных для каждого теста и выключался в конце. Эти тесты могут выполняться на машинах разработки, а также на сервере CI, поэтому мое решение также должно быть переносимым .

Может ли кто-нибудь с дополнительными знаниями о MongoDB помочь мне понять осуществимость этого подхода и / или, возможно, предложить какие-либо материалы для чтения, которые могут помочь мне начать работу?

Я также открыт для других предложений о том, как я могу подойти к этой проблеме ...


Если вы используете maven, вы можете использовать наш mvnrepository.com/artifact/com.wenzani/mongodb-maven-plugin
markdsievers

Вы также можете проверить этот проект, который имитирует MongoDB внутри памяти JVM. github.com/thiloplanz/jmockmongo Но он все еще находится в разработке.
Себастьен Лорбер

Не [только] для модульного тестирования, но прочтите этот пост в блоге, если вы хотите запустить MongoDB (даже кластер) как развертывание в памяти, если вы используете Linux. edgystuff.tumblr.com/post/49304254688 Было бы здорово иметь его прямо из коробки, как RavenDB.
Тамир

Подобно упомянутому здесь модулю embedmongo-maven-plugin, также доступен подключаемый модуль Gradle Mongo . Как и плагин Maven, он также включает в себя api flapdoodle EmbeddedMongoDb и позволяет запускать управляемый экземпляр Mongo из ваших сборок Gradle.
Роберт Тейлор

Посмотрите этот пример кода здесь: github.com/familysyan/embedded-mongo-integ . Никакой установки, никакой зависимости. Это просто платформенно-независимый ant-скрипт, который загружается и настраивается за вас. Он также очищает все после ваших тестов.
Эдмонд

Ответы:


9

Вот обновленная (на 2019 год) версия принятого ответа от @rozky (многое было изменено как в библиотеках Mongo, так и в 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
Многократный запуск и остановка Embedded mongo для каждого теста не дает результатов в большинстве тестов. Лучше начать до всех тестов и выключить, когда все они будут выполнены
DBS

Вам необходимо включить @DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS)вместе с указанным выше изменением
DBS

@DBS Вы также можете использовать случайный порт, чтобы вы все еще могли запускать свои тесты одновременно на новом встроенном экземпляре mongo. Смотрите документацию здесь .
Collin Krawll

95

Я нашел встроенную библиотеку MongoDB, которая выглядит многообещающей и делает то, о чем вы просили.

В настоящее время поддерживает версии MongoDB: 1.6.5до 3.1.6, при условии, что двоичные файлы по-прежнему доступны с настроенного зеркала.

Вот небольшой пример использования, который я только что попробовал, и он отлично работает:

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
Просто использовал эту библиотеку, и она отлично сработала. JUnit тестировал Mongo API на Mac. Рекомендуемые.
Мартин Доу

1
+1 отличная находка! Когда я впервые начал использовать mongodb год назад, отсутствие программного способа тестирования базы данных было одним из недостатков. Мы обошли это, установив тестовый экземпляр в каждой среде, настроенный с помощью файла свойств Java, но, конечно, для этого требовалось, чтобы mongo был установлен в каждой среде. Похоже, это все решит.
Andyb

Ницца! удалил свой ответ, так как он больше не точен. Кто-нибудь знает, насколько это зрело? Я могу представить, что имитация MongoDB на очень низком уровне была бы довольно сложной, и, судя по источнику, это выглядит довольно высоким уровнем.
Ремон ван Влит

Наконец-то я поигрался с этим в моем проекте и могу сообщить, что его невероятно легко настроить и запустить. Все низкоуровневые вызовы являются частью официального com.mongodb Java API, поэтому это не сложнее, чем использование обычного API.
Andyb

17
Будьте осторожны с этим раствором. Он просто собирает информацию о текущей операционной системе и загружает соответствующие двоичные файлы MongoDB для конкретной платформы из Интернета, запускает демон и выполняет некоторые другие настройки. В качестве корпоративного решения это не так. Издевательство может быть единственным реальным вариантом.
Джеймс Уоткинс

18

Есть продукт Foursquare Fongo . Fongo - это java-реализация mongo в памяти. Он перехватывает вызовы стандартного mongo-java-драйвера для поиска, обновления, вставки, удаления и других методов. В основном используется для облегченного модульного тестирования, когда вы не хотите раскручивать монго-процесс.


1
Может ли Fongo перехватывать вызовы в сеть, например, на localhost: 27017, чтобы он мог служить поддельным сервером, позволяющим интегрировать тестирование без изменения кода?

mongo-java-server - это реализация поддельного сервера, которую можно использовать для тестирования интеграции без изменения кода.
Бенедикт Вальдфогель

7

Если вы используете Maven, вас может заинтересовать созданный мной плагин, который обертывает встроенный mongo API flapdoodle.de :

встроенный плагин mongo-maven

Он обеспечивает startцель, которую вы можете использовать для запуска любой версии MongoDB, которую хотите (например, во время pre-integration-test), и stopцель, которая остановит MongoDB (например, во время post-integration-test).

Настоящее преимущество использования этого плагина по сравнению с другими заключается в том, что MongoDB не требуется предварительно устанавливать. Бинарные файлы MongoDB загружаются и сохраняются ~/.embedmongoдля будущих сборок.


А вот версия Clojure для Leiningen: github.com/joelittlejohn/lein-embongo
joelittlejohn


4

с Spring-boot 1.3 вы можете использовать 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
можете ли вы объяснить, что на самом деле делает аннотация "@EnableAutoConfiguration (exclude = {EmbeddedMongoAutoConfiguration.class})"?
Бруно Неграу Зика,

Причина, скорее всего, в том, что зависимость de.flapdoodle.embed.mongo не отмечена для области тестирования. Чтобы не забирать его и не запускать встроенный mongo в настройке производственного приложения, необходимо исключение.
Сергей Щербаков

3

Начиная с версии 3.2.6, MongoDB можно запускать в памяти. С сайта :

Начиная с MongoDB Enterprise версии 3.2.6, механизм хранения в памяти является частью общей доступности (GA) в 64-битных сборках. За исключением некоторых метаданных и диагностических данных, механизм хранения в памяти не поддерживает никаких данных на диске, включая данные конфигурации, индексы, учетные данные пользователя и т. Д.


1

Не только для модульного тестирования, но также объяснил, как использовать inmemory mongodb с rest api.

зависимость 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

открытый интерфейс UserRepository расширяет MongoRepository {

}

для справки и всего кода Java используйте ссылку ниже: (пошаговое объяснение)

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


0

Выступления лучше, если исполнять mongodсstorageEngine='ephemeralForTest'

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

-1

В производстве вы будете использовать настоящую базу данных.

Если вы хотите, чтобы ваши тесты отражали поведение вашего продукта в производственной среде, используйте реальный экземпляр Mongo.

Поддельная реализация может не вести себя точно так же, как настоящая. При тестировании следует стремиться к корректности. На втором месте - скорость исполнения.


6
Я думаю, вы упустили мою цель. Я не искал поддельный экземпляр Mongo, мне нужен был настоящий экземпляр, но встроенный в мои тесты. Причина заключалась в том, чтобы запустить MongoDB и перевести его в определенное состояние, не загрязняя существующую базу данных, запустить серию операций, а затем проверить результат без необходимости просеивать произвольные данные, не связанные с моим тестом. Настолько реально, насколько это возможно, при этом сохраняя контролируемую среду тестирования.
seanhodges

Извините, слово «моделировать» и все эти предложения «в памяти» заставили меня забыть значение слова «встроенный» в Java-land. Рад это слышать.
Джексон
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.