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

36

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

这里的想法是让集成测试(目前为空)连接到嵌入式MongoDB实例,而不是“live”的实例。然而,它没有起作用。我可以看到测试连接到MongoDB的“live”实例,如果我关闭它,构建将会失败,因为它仍在尝试连接到MongoDB的live实例。有人知道这是为什么吗?我该如何使测试连接到嵌入式实例?


5
坦率地说,我一整天都在尝试注解和设置,但并没有成功。修改一个 Spring Boot 应用程序以便进行测试似乎就像拔牙一样困难。 - Jon
现在,只要有Maven依赖项de.flapdoodle.embed:de.flapdoodle.embed.mongo,你可能只需要spring.data.mongodb.portspring.mongodb.embedded.* - aksh1618
5个回答

41
自Spring Boot版本1.3以来,就有一个EmbeddedMongoAutoConfiguration类可以直接使用。这意味着您根本不需要创建配置文件,如果您想要更改一些内容,仍然可以进行更改。

添加了嵌入式MongoDB的自动配置。只需依赖于de.flapdoodle.embed:de.flapdoodle.embed.mongo就可以开始使用。可以通过application.properties控制配置,例如要使用的Mongo版本。请参阅文档以获取更多信息。(Spring Boot发布说明)

必须添加到application.properties文件中的最基本和重要的配置是
spring.data.mongodb.port=0(0表示将随机选择空闲端口)

有关更多详细信息,请参见:Spring Boot文档MongoDb


3
谢谢。spring.data.mongodb.port=0很重要——如果没有它,当同时运行许多测试时,一些测试会随机失败。 - Marwin
它仍然有效。就测试而言,对于Spring Boot 1.5的嵌入式Mongo配置没有任何改变。只需确保添加与mongo驱动程序版本相对应的正确版本的flapdoodle.embed.mongo即可。 - magiccrafter
只是添加 'de.flapdoodle.embed.mongo' 依赖项,它并不起作用。有人可以提供一些代码示例吗? - sintetico82
1
你需要在类路径中同时包含de.flapdoodle.embed.mongocom.mongodb:https://github.com/spring-projects/spring-boot/blob/master/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mongo/embedded/EmbeddedMongoAutoConfiguration.java#L82 - Cisco
1
找到了:spring.mongodb.embedded.version。完整文档请参考https://docs.spring.io/spring-boot/docs/current/reference/html/common-application-properties.html。 - lorraine batol
显示剩余2条评论

18

编辑: 对于使用EmbeddedMongoAutoConfiguration的Spring Boot 1.3+,请参见magiccrafter的回答

如果由于任何原因无法使用它,请继续阅读。


测试类:

    @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>

1
结果发现我漏掉的是@ SpringApplicationConfiguration(classes = {Application.class,TestMongoConfig.class})的后半部分-我没有意识到您还需要在注释中添加配置才能将其拾取。 - Jon
4
我跟着你的实现走了一遍,但当我在 POM 中指定范围为 test 时,它对我不起作用。在构建应用程序时,我收到了错误信息"Caused by: java.lang.ClassNotFoundException: de.flapdoodle.embed.mongo.distribution.IFeatureAwareVersion",当我从 POM 中移除测试范围时,我会得到以下错误信息"NoSuchBeanDefinitionException: No bean named 'embeddedMongoServer' is defined"。请问如何解决? - naoru
我遇到了“Error creating bean with name 'testMongoConfig',No qualifying bean error”的问题。 - Francis Zabala
1
存储库获取实际的集合配置,而不是从TestMongoConfig类获取配置。有谁能帮我解决这个问题? - devanathan

8
在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>

如果您想测试访问嵌入式Mongo数据库的控制器? - sintetico82
你可以像使用真正的 MongoDB 一样注入 MongoClient 实例,也可以使用 MongoTemplate - bigZee77

5

I'll complete previous answer

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{
}

这是我在Mongo配置中所需的内容,以使其停止杀死我的Swagger2测试(随后的测试将无法正确启动) 上述项目让我开始思考,最终对我来说就是这样。Spring版本4.x,Spring自动配置1.3.2 @EnableAutoConfiguration(exclude={EmbeddedMongoAutoConfiguration.class - ken

3

请确保在使用@ComponentScan时明确指定了要扫描的内容。

如果没有定义特定的包,将从具有此注释的类所在的包进行扫描。(详见@ComponentScan Javadoc)

因此,如果您的TestProductApplicationProductApplication配置都位于同一个包中,则可能会出现Spring正在扫描您的ProductApplication配置并使用它。

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


活动配置文件注释可能是个好主意。我会着手处理的。 - Jon

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接