如何在Spring Boot应用程序中配置嵌入式MongDB进行集成测试?

时间:2022-09-03 19:50:19

I have a fairly simple Spring Boot application which exposes a small REST API and retrieves data from an instance of MongoDB. Queries to the MongoDB instance go through a Spring Data based repository. Some key bits of code below.

我有一个相当简单的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);
    }
}

So the idea here is to have the integration tests (empty at the moment) connect to the embedded mongo instance and not the "live" one. However, it doesn't work. I can see the tests connecting to the "live" instance of Mongo, and if I shut that down the build simply fails as it is still attempting to connect to the live instance of Mongo. Does anyone know why this is? How do I get the tests to connect to the embedded instance?

所以这里的想法是让集成测试(此时为空)连接到嵌入式mongo实例而不是“实时”实例。但是,它不起作用。我可以看到测试连接到Mongo的“实时”实例,如果我关闭它,构建就会失败,因为它仍然试图连接到Mongo的实例。有人知道为什么吗?如何让测试连接到嵌入式实例?

5 个解决方案

#1


17  

This works for me.

这对我有用。

Test class:

测试类:

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

    } 

Simple MongoDB repository:

简单的MongoDB存储库:

public interface GameRepository extends MongoRepository<Game, String>     {

    Game findByName(String name);
}

MongoDB test configuration:

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

的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


26  

Since Spring Boot version 1.3 there is an EmbeddedMongoAutoConfiguration class which comes out of the box. This means that you don't have to create a configuration file at all and if you want to change things you still can.

从Spring Boot 1.3版开始,就有一个EmbeddedMongoAutoConfiguration类,它开箱即用。这意味着您根本不必创建配置文件,并且如果您想要更改仍然可以的内容。

Auto-configuration for Embedded MongoDB has been added. A dependency on de.flapdoodle.embed:de.flapdoodle.embed.mongo is all that’s necessary to get started. Configuration, such as the version of Mongo to use, can be controlled via application.properties. Please see the documentation for further information. (Spring Boot Release Notes)

添加了嵌入式MongoDB的自动配置。对de.flapdoodle.embed:de.flapdoodle.embed.mongo的依赖是开始所需的全部内容。可以通过application.properties控制配置,例如要使用的Mongo版本。有关详细信息,请参阅文档。 (Spring Boot发行说明)

The most basic and important configuration that has to be added to the application.properties files is
spring.data.mongodb.port=0 (0 means that it will be selected randomly from the free ones)

必须添加到application.properties文件的最基本和最重要的配置是spring.data.mongodb.port = 0(0表示将从免费的随机选择它)

for more details check: Spring Boot Docs MongoDb

有关更多详细信息,请查看:Spring Boot Docs MongoDb

#3


5  

I'll complete previous answer

我将完成以前的答案

pom.xml

的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

MongoConfig

@Configuration
@EnableAutoConfiguration(exclude = { EmbeddedMongoAutoConfiguration.class })
public class MongoConfig{
}

#4


5  

In version 1.5.7 use just this:

在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");
    }

}

And

        <dependency>
            <groupId>de.flapdoodle.embed</groupId>
            <artifactId>de.flapdoodle.embed.mongo</artifactId>
        </dependency>

#5


3  

Make sure you are explicit with your @ComponentScan. By default,

确保你明确使用@ComponentScan。默认,

If specific packages are not defined scanning will occur from the package of the class with this annotation. (@ComponentScan Javadoc)

如果未定义特定包,则将使用此批注从类的包中进行扫描。 (@ComponentScan Javadoc)

Therefore, if your TestProductApplication and ProductApplication configurations are both in the same package, it is possible Spring is component-scanning your ProductApplication configuration and using that.

因此,如果您的TestProductApplication和ProductApplication配置都在同一个包中,则Spring可能会对您的ProductApplication配置进行组件扫描并使用它。

Additionally, I would recommend putting your Test mongo beans into a 'test' or 'local' profile and using the @ActiveProfiles annotation in your test class to enable the test/local profile.

另外,我建议将Test mongo bean放入'test'或'local'配置文件中,并在测试类中使用@ActiveProfiles注释来启用测试/本地配置文件。

#1


17  

This works for me.

这对我有用。

Test class:

测试类:

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

    } 

Simple MongoDB repository:

简单的MongoDB存储库:

public interface GameRepository extends MongoRepository<Game, String>     {

    Game findByName(String name);
}

MongoDB test configuration:

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

的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


26  

Since Spring Boot version 1.3 there is an EmbeddedMongoAutoConfiguration class which comes out of the box. This means that you don't have to create a configuration file at all and if you want to change things you still can.

从Spring Boot 1.3版开始,就有一个EmbeddedMongoAutoConfiguration类,它开箱即用。这意味着您根本不必创建配置文件,并且如果您想要更改仍然可以的内容。

Auto-configuration for Embedded MongoDB has been added. A dependency on de.flapdoodle.embed:de.flapdoodle.embed.mongo is all that’s necessary to get started. Configuration, such as the version of Mongo to use, can be controlled via application.properties. Please see the documentation for further information. (Spring Boot Release Notes)

添加了嵌入式MongoDB的自动配置。对de.flapdoodle.embed:de.flapdoodle.embed.mongo的依赖是开始所需的全部内容。可以通过application.properties控制配置,例如要使用的Mongo版本。有关详细信息,请参阅文档。 (Spring Boot发行说明)

The most basic and important configuration that has to be added to the application.properties files is
spring.data.mongodb.port=0 (0 means that it will be selected randomly from the free ones)

必须添加到application.properties文件的最基本和最重要的配置是spring.data.mongodb.port = 0(0表示将从免费的随机选择它)

for more details check: Spring Boot Docs MongoDb

有关更多详细信息,请查看:Spring Boot Docs MongoDb

#3


5  

I'll complete previous answer

我将完成以前的答案

pom.xml

的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

MongoConfig

@Configuration
@EnableAutoConfiguration(exclude = { EmbeddedMongoAutoConfiguration.class })
public class MongoConfig{
}

#4


5  

In version 1.5.7 use just this:

在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");
    }

}

And

        <dependency>
            <groupId>de.flapdoodle.embed</groupId>
            <artifactId>de.flapdoodle.embed.mongo</artifactId>
        </dependency>

#5


3  

Make sure you are explicit with your @ComponentScan. By default,

确保你明确使用@ComponentScan。默认,

If specific packages are not defined scanning will occur from the package of the class with this annotation. (@ComponentScan Javadoc)

如果未定义特定包,则将使用此批注从类的包中进行扫描。 (@ComponentScan Javadoc)

Therefore, if your TestProductApplication and ProductApplication configurations are both in the same package, it is possible Spring is component-scanning your ProductApplication configuration and using that.

因此,如果您的TestProductApplication和ProductApplication配置都在同一个包中,则Spring可能会对您的ProductApplication配置进行组件扫描并使用它。

Additionally, I would recommend putting your Test mongo beans into a 'test' or 'local' profile and using the @ActiveProfiles annotation in your test class to enable the test/local profile.

另外,我建议将Test mongo bean放入'test'或'local'配置文件中,并在测试类中使用@ActiveProfiles注释来启用测试/本地配置文件。