Модульное тестирование с MongoDB


моя база данных выбора-MongoDB. Я пишу API уровня данных для абстрактной реализации деталей из клиентских приложений - то есть, я по существу предоставляю единый открытый интерфейс (объект, который действует как IDL).

Я проверяю свою логику, как я иду в TDD образом. Перед каждым модульным тестом,@Before метод вызывается для создания синглтона базы данных, после чего, когда тест завершается,@After метод вызывается для удаления базы данных. Это способствует независимость среди модульных тестов.

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

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

что делают другие в таких ситуациях? То есть, как люди тестируют модульный код, который взаимодействует с базой данных?

кроме того, мой публичный интерфейс подключается к базе данных, определенной во внешнем файле конфигурации - кажется неправильным использовать это соединение для моего модульного тестирования - опять же, ситуация, которая выиграет от какой-то насмешки?

4 52

4 ответа:

Как писал sbridges в этом посте, это плохая идея не иметь выделенную службу (иногда также известную как репозиторий или DAO), которая абстрагирует доступ к данным из логики. Затем вы можете проверить логику, предоставив макет DAO.

другой подход, который я делаю, заключается в создании макета объекта Mongo (например, PowerMockito), а затем возвращает соответствующие результаты. Это потому, что вам не нужно проверять, работает ли база данных в модульных тестах, но более того, вы должны проверить, если правильный запрос был отправлен в базу данных.

Mongo mongo = PowerMockito.mock(Mongo.class);
DB db = PowerMockito.mock(DB.class);
DBCollection dbCollection = PowerMockito.mock(DBCollection.class);

PowerMockito.when(mongo.getDB("foo")).thenReturn(db);
PowerMockito.when(db.getCollection("bar")).thenReturn(dbCollection);

MyService svc = new MyService(mongo); // Use some kind of dependency injection
svc.getObjectById(1);

PowerMockito.verify(dbCollection).findOne(new BasicDBObject("_id", 1));

Это тоже вариант. Конечно, создание насмешек и возвращение соответствующих объектов просто кодируется как пример выше.

технически тесты, которые разговаривают с базой данных (nosql или иначе), не являются тесты, поскольку тесты тестируют взаимодействие с внешней системой, а не только тестирование изолированной единицы кода. Однако тесты, которые обращаются к базе данных, часто чрезвычайно полезны и часто достаточно быстры для выполнения с другими модульными тестами.

обычно у меня есть интерфейс службы (например, UserService), который инкапсулирует всю логику для работы с базой данных. Код, который полагается на приложение UserService можете использовать глумились версия приложения UserService и это легко проверить.

при тестировании реализации службы, которая разговаривает с Mongo, (например, MongoUserService) проще всего написать некоторый код java, который запустит / остановит процесс mongo на локальной машине, и ваш MongoUserService подключится к этому, см. Это вопрос для заметки.

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

Я написал реализацию заглушки MongoDB в Java:mongo-java-server

Default-это бэкэнд в памяти, который можно легко использовать в модульных и интеграционных тестах.

пример

MongoServer server = new MongoServer(new MemoryBackend());
// bind on a random local port
InetSocketAddress serverAddress = server.bind();

MongoClient client = new MongoClient(new ServerAddress(serverAddress));

DBCollection coll = client.getDB("testdb").getCollection("testcoll");
// creates the database and collection in memory and inserts the object
coll.insert(new BasicDBObject("key", "value"));

assertEquals(1, collection.count());
assertEquals("value", collection.findOne().get("key"));

client.close();
server.shutdownNow();

Я удивлен, что никто не посоветовал использовать fakemongo до сих пор. Он эмулирует клиент mongo довольно хорошо, и все это работает на одном JVM с тестами - поэтому интеграционные тесты становятся надежными и технически гораздо более близкими к истинным "модульным тестам", поскольку никакого внешнего системного взаимодействия не происходит. Это похоже на использование встроенного H2 для модульного тестирования кода SQL. Я был очень доволен использованием fakemongo в модульных тестах, которые тестируют код интеграции базы данных в сквозном режиме. Рассмотрим эту конфигурацию в тесте весенний контекст:

@Configuration
@Slf4j
public class FongoConfig extends AbstractMongoConfiguration {
    @Override
    public String getDatabaseName() {
        return "mongo-test";
    }

    @Override
    @Bean
    public Mongo mongo() throws Exception {
        log.info("Creating Fake Mongo instance");
        return new Fongo("mongo-test").getMongo();
    }

    @Bean
    @Override
    public MongoTemplate mongoTemplate() throws Exception {
        return new MongoTemplate(mongo(), getDatabaseName());
    }

}

С помощью этого вы можете проверить свой код, который использует MongoTemplate из контекста spring, и в сочетании с nosql-unit,jsonunit и т. д. вы получаете надежные модульные тесты, которые охватывают код запроса mongo.

@Test
@UsingDataSet(locations = {"/TSDR1326-data/TSDR1326-subject.json"}, loadStrategy = LoadStrategyEnum.CLEAN_INSERT)
@DatabaseSetup({"/TSDR1326-data/dbunit-TSDR1326.xml"})
public void shouldCleanUploadSubjectCollection() throws Exception {
    //given
    JobParameters jobParameters = new JobParametersBuilder()
            .addString("studyId", "TSDR1326")
            .addString("execId", UUID.randomUUID().toString())
            .toJobParameters();

    //when
    //next line runs a Spring Batch ETL process loading data from SQL DB(H2) into Mongo
    final JobExecution res = jobLauncherTestUtils.launchJob(jobParameters);

    //then
    assertThat(res.getExitStatus()).isEqualTo(ExitStatus.COMPLETED);
    final String resultJson = mongoTemplate.find(new Query().with(new Sort(Sort.Direction.ASC, "topLevel.subjectId.value")),
            DBObject.class, "subject").toString();

    assertThatJson(resultJson).isArray().ofLength(3);
    assertThatDateNode(resultJson, "[0].topLevel.timestamp.value").isEqualTo(res.getStartTime());

    assertThatNode(resultJson, "[0].topLevel.subjectECode.value").isStringEqualTo("E01");
    assertThatDateNode(resultJson, "[0].topLevel.subjectECode.timestamp").isEqualTo(res.getStartTime());

    ... etc
}

я использовал fakemongo без проблем с драйвером mongo 3.4, и сообщество действительно близко к выпуску версии, которая поддерживает драйвер 3.6 (https://github.com/fakemongo/fongo/issues/316).