Как настроить Embedded MongDB для тестирования интеграции в приложении Spring Boot?
У меня довольно простое приложение Spring Boot, которое предоставляет небольшой REST API и извлекает данные из экземпляра MongoDB. Запросы к экземпляру MongoDB проходят через репозиторий Spring Data. Некоторые ключевые биты кода ниже.
// Main application class
@EnableAutoConfiguration(exclude={MongoAutoConfiguration.class, MongoDataAutoConfiguration.class})
@ComponentScan
@Import(MongoConfig.class)
public class ProductApplication {
public static void main(String[] args) {
SpringApplication.run(ProductApplication.class, args);
}
}
// Product repository with Spring data
public interface ProductRepository extends MongoRepository<Product, String> {
Page<Product> findAll(Pageable pageable);
Optional<Product> findByLineNumber(String lineNumber);
}
// Configuration for "live" connections
@Configuration
public class MongoConfig {
@Value("${product.mongo.host}")
private String mongoHost;
@Value("${product.mongo.port}")
private String mongoPort;
@Value("${product.mongo.database}")
private String mongoDatabase;
@Bean(name="mongoClient")
public MongoClient mongoClient() throws IOException {
return new MongoClient(mongoHost, Integer.parseInt(mongoPort));
}
@Autowired
@Bean(name="mongoDbFactory")
public MongoDbFactory mongoDbFactory(MongoClient mongoClient) {
return new SimpleMongoDbFactory(mongoClient, mongoDatabase);
}
@Autowired
@Bean(name="mongoTemplate")
public MongoTemplate mongoTemplate(MongoClient mongoClient) {
return new MongoTemplate(mongoClient, mongoDatabase);
}
}
@Configuration
@EnableMongoRepositories
public class EmbeddedMongoConfig {
private static final String DB_NAME = "integrationTest";
private static final int DB_PORT = 12345;
private static final String DB_HOST = "localhost";
private static final String DB_COLLECTION = "products";
private MongodExecutable mongodExecutable = null;
@Bean(name="mongoClient")
public MongoClient mongoClient() throws IOException {
// Lots of calls here to de.flapdoodle.embed.mongo code base to
// create an embedded db and insert some JSON data
}
@Autowired
@Bean(name="mongoDbFactory")
public MongoDbFactory mongoDbFactory(MongoClient mongoClient) {
return new SimpleMongoDbFactory(mongoClient, DB_NAME);
}
@Autowired
@Bean(name="mongoTemplate")
public MongoTemplate mongoTemplate(MongoClient mongoClient) {
return new MongoTemplate(mongoClient, DB_NAME);
}
@PreDestroy
public void shutdownEmbeddedMongoDB() {
if (this.mongodExecutable != null) {
this.mongodExecutable.stop();
}
}
}
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = TestProductApplication.class)
@IntegrationTest
@WebAppConfiguration
public class WtrProductApplicationTests {
@Test
public void contextLoads() {
// Tests empty for now
}
}
@EnableAutoConfiguration(exclude={MongoAutoConfiguration.class, MongoDataAutoConfiguration.class})
@ComponentScan
@Import(EmbeddedMongoConfig.class)
public class TestProductApplication {
public static void main(String[] args) {
SpringApplication.run(TestProductApplication.class, args);
}
}
Поэтому идея состоит в том, чтобы интеграционные тесты (пустые на данный момент) соединялись со встроенным экземпляром mongo, а не с "живым". Однако это не работает. Я вижу тесты, связанные с "живым" экземпляром Mongo, и если я закрываю это, сборка просто терпит неудачу, поскольку он все еще пытается подключиться к живому экземпляру Mongo. Кто-нибудь знает, почему это? Как мне получить тесты для подключения к встроенному экземпляру?
Ответы
Ответ 1
Это работает для меня.
Класс испытания:
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = {
Application.class,
TestMongoConfig.class // <--- Don't forget THIS
})
public class GameRepositoryTest {
@Autowired
private GameRepository gameRepository;
@Test
public void shouldCreateGame() {
Game game = new Game(null, "Far Cry 3");
Game gameCreated = gameRepository.save(game);
assertEquals(gameCreated.getGameId(), gameCreated.getGameId());
assertEquals(game.getName(), gameCreated.getName());
}
}
Простой репозиторий MongoDB:
public interface GameRepository extends MongoRepository<Game, String> {
Game findByName(String name);
}
Конфигурация теста MongoDB:
import com.mongodb.Mongo;
import com.mongodb.MongoClientOptions;
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 org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.mongo.MongoProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.io.IOException;
@Configuration
public class TestMongoConfig {
@Autowired
private MongoProperties properties;
@Autowired(required = false)
private MongoClientOptions options;
@Bean(destroyMethod = "close")
public Mongo mongo(MongodProcess mongodProcess) throws IOException {
Net net = mongodProcess.getConfig().net();
properties.setHost(net.getServerAddress().getHostName());
properties.setPort(net.getPort());
return properties.createMongoClient(this.options);
}
@Bean(destroyMethod = "stop")
public MongodProcess mongodProcess(MongodExecutable mongodExecutable) throws IOException {
return mongodExecutable.start();
}
@Bean(destroyMethod = "stop")
public MongodExecutable mongodExecutable(MongodStarter mongodStarter, IMongodConfig iMongodConfig) throws IOException {
return mongodStarter.prepare(iMongodConfig);
}
@Bean
public IMongodConfig mongodConfig() throws IOException {
return new MongodConfigBuilder().version(Version.Main.PRODUCTION).build();
}
@Bean
public MongodStarter mongodStarter() {
return MongodStarter.getDefaultInstance();
}
}
pom.xml
<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>1.48.0</version>
<scope>test</scope>
</dependency>
Ответ 2
Начиная с версии Spring Boot версии 1.3 существует класс EmbeddedMongoAutoConfiguration
который выходит из коробки. Это означает, что вам вообще не нужно создавать файл конфигурации, и если вы хотите изменить все, что вы еще можете.
Добавлена автоматическая настройка встроенного MongoDB. Зависимость от de.flapdoodle.embed: de.flapdoodle.embed.mongo - все, что необходимо для начала работы. Конфигурация, например, версия Mongo для использования, можно контролировать с помощью application.properties. Дополнительную информацию см. В документации. (Заметки о выпуске Spring Boot)
Самая простая и важная конфигурация, которая должна быть добавлена в файлы application.properties, - это
spring.data.mongodb.port=0
(0 означает, что он будет выбран случайным образом из свободных)
для более подробной информации: Spring Boot Docs MongoDb
Ответ 3
Я закончу предыдущий ответ
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{
}
Ответ 4
В версии 1.5.7 используйте только это:
@RunWith(SpringRunner.class)
@DataMongoTest
public class UserRepositoryTests {
@Autowired
UserRepository repository;
@Before
public void setUp() {
User user = new User();
user.setName("test");
repository.save(user);
}
@Test
public void findByName() {
List<User> result = repository.findByName("test");
assertThat(result).hasSize(1).extracting("name").contains("test");
}
}
А также
<dependency>
<groupId>de.flapdoodle.embed</groupId>
<artifactId>de.flapdoodle.embed.mongo</artifactId>
</dependency>
Ответ 5
Убедитесь, что вы явно используете ваш @ComponentScan
. По умолчанию,
Если определенные пакеты не определены, сканирование будет происходить из пакета класса с помощью этой аннотации. (@ComponentScan Javadoc)
Поэтому, если ваши конфигурации TestProductApplication
и ProductApplication
находятся в одном пакете, возможно, Spring - компонентное сканирование конфигурации ProductApplication
и использование этого.
Кроме того, я бы рекомендовал поместить ваши тестовые монго-бобы в профиль "test" или "local" и использовать аннотацию @ActiveProfiles в вашем тестовом классе, чтобы включить тестовый/локальный профиль.