Модульное тестирование с MongoDB
моя база данных выбора-MongoDB. Я пишу API уровня данных для абстрактной реализации деталей из клиентских приложений - то есть, я по существу предоставляю единый открытый интерфейс (объект, который действует как IDL).
Я проверяю свою логику, как я иду в TDD образом. Перед каждым модульным тестом,@Before
метод вызывается для создания синглтона базы данных, после чего, когда тест завершается,@After
метод вызывается для удаления базы данных. Это способствует независимость среди модульных тестов.
почти все модульные тесты, т. е. выполнение контекстного запроса, требуется какая-то логика вставки, чтобы произойти перед рукой. Мой открытый интерфейс предоставляет метод вставки-тем не менее, кажется неправильным использовать этот метод в качестве логики предшественника для каждого модульного теста.
На самом деле мне нужен какой-то механизм издевательства, но у меня не было большого опыта работы с издевательскими фреймворками, и кажется, что Google ничего не возвращает в издевательскую структуру можно было бы использовать с MongoDB.
что делают другие в таких ситуациях? То есть, как люди тестируют модульный код, который взаимодействует с базой данных?
кроме того, мой публичный интерфейс подключается к базе данных, определенной во внешнем файле конфигурации - кажется неправильным использовать это соединение для моего модульного тестирования - опять же, ситуация, которая выиграет от какой-то насмешки?
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).