使用Spring Data&amp ;;进行单元/集成测试MongoDB无法模拟存储库

时间:2016-01-05 12:19:41

标签: java spring mongodb unit-testing spring-mvc

事先有一些小信息:

  • 这是关于端到端测试,它指的是现在与几个模块的集成测试。
  • 由于以前遇到过一些困难,我们必须更改大量的测试代码,而我正在努力降低集成度,以便我们回到单元测试。但是,在允许减少配置等之前,我需要先让所有内容恢复正常。
  • 如果您阅读了最后一段,您已经知道了,但无论如何:我知道这不是一个好方法,我正在努力改变它,但我必须先这样做。

依赖关系

Spring Boot 1.3.0
Spring Mongo 1.3.3
Spring Security 3.1.4
Spring Security Cas 4.0.2
Flapdoodle Embedmongo 1.46.0

现在,我们有一个用

注释的抽象测试类

@RunWith(SpringJUnit4ClassRunner.class)@SpringApplicationConfiguration(TestConfig.class)@WebAppConfiguration

TestConfig类看起来像这样:

@Configuration
@EnableAutoConfiguration(exclude = { EmbeddedMongoAutoConfiguration.class })
@ComponentScan(value = { "package1", "package2" })
public class TestConfig {

}

如您所见,它会扫描这两个包中的所有内容,并同时提取其他几个配置类:

@Configuration
// needed if working with Spring-Data-Repository interfaces and subprojects
// http://stackoverflow.com/questions/29084824/spring-repository-components-not-found-in-gradle-subproject-springboot
@EnableMongoRepositories({ "package2.subpackage" })
public class ModelConfiguration {

}

@Configuration
@EnableAutoConfiguration(exclude = { EmbeddedMongoAutoConfiguration.class })
@EnableMongoRepositories(basePackages = { "package2" })
public class TestMongoConfiguration {

    private static final String DESTROY_METHOD_CLOSE = "close";
    private static final String DESTROY_METHOD_STOP = "stop";

    private static final MongodStarter STARTER = MongodStarter.getDefaultInstance();

    @Autowired
    private MongoProperties mongoProperties;

    @Autowired(required = false)
    private MongoClientOptions mongoClientOptions;

    @Autowired
    private Environment environment;

    @Bean(destroyMethod = DESTROY_METHOD_CLOSE)
    public MongoClient mongo() throws IOException {
        Net net = mongodProcess().getConfig().net();
        mongoProperties.setHost(net.getServerAddress().getHostName());
        mongoProperties.setPort(net.getPort());
        return mongoProperties.createMongoClient(this.mongoClientOptions, environment);
    }

    @Bean(destroyMethod = DESTROY_METHOD_STOP)
    public MongodProcess mongodProcess() throws IOException {
        return mongodExecutable().start();
    }

    @Bean(destroyMethod = DESTROY_METHOD_STOP)
    public MongodExecutable mongodExecutable() throws IOException {
        return STARTER.prepare(mongodConfig());
    }

    @Bean
    public IMongodConfig mongodConfig() throws IOException {
        return new MongodConfigBuilder().version(Version.Main.PRODUCTION).build();
    }
}

这是我的测试课程的一部分:

@SpringApplicationConfiguration(classes = TestClass.TestConfiguration.class)
public class TestClass extends AbstractTest {

    @Order(Ordered.HIGHEST_PRECEDENCE)
    public static class TestConfiguration {
        @Bean
        @Primary
        public FooRepository fooRepository() {
            FooRepository mock = mock(FooRepository.class);
            // mockbehaviour
            return mock;
        }

        @Bean
        @Primary
        public FooDao fooDao() {
            FooDao mock = mock(FooDao.class);
            //Mock behaviour
            return mock;
        }

        @Bean
        @Primary
        public BarDao barDao() {
            BarDao mock = mock(BarDao.class);
            //MockBehaviour
            return mock;
        }

        @Bean
        @Primary
        public BarRepository barRepository() {
            BarRepository mock = mock(BarRepository.class);
            return mock;
        }
    }

我现在正在创建几个请求,这些请求被传递给超类进行安全测试,并且(因为所有测试只有一个成功案例)然后我尝试通过verify(mock)验证对Daos和Repositories的相应调用。

Beans通过@Resource注入Controller,就像我在测试类中使用它一样。

问题

由于某种原因,Daos正在按预期工作(已创建,行为被指定,被调用并且可以被验证),存储库不是 - 创建和使用常规代理。在使用@Order(Ordered.HIGHEST_PRECEDENCE)之前,简单地跳过了bean创建(并且这样记录),因为已经注册的顶级bean被拒绝被覆盖。

在诉诸于此之后,Repository-beans已被覆盖而不是被跳过(当然,已经被记录)。 bean级别的@Primary应该确保使用模拟而不是组件扫描创建的实际bean - 以及其他测试实例,它们不启动整个应用程序和嵌入式Mongo,这是有效的按照预期 - 但在这里,它没有。

我尝试过的事情

  • 设置@Order
  • 排除@EnableMongoRepositories - 配置
  • 手动注入创建的Mocks
  • 彻底的网络研究
  • 设置bean角色
  • 重新创建bean
  • 创建dao-bean时触发bean创建

我一直在寻找好几天,总是和我偶然发现的唯一可能的解决方案是:创建一个xml配置(这是一个禁忌,因为我们应该创建一切而不需要xml-文件),使用Fongo(这将需要为这一步导入一个新的依赖,因为数据库测试完全在其他地方发生,然后删除它),或禁用bean覆盖(我完全不知道做并且Spring文档并没有真正帮助 - 是的,方法就在那里,但每当我尝试这样做时,它都不会影响当前的上下文。)

1 个答案:

答案 0 :(得分:3)

对不起,这会更适合评论而不是答案,但格式太糟糕了......

不知道为什么它不起作用,但你是否尝试使用@Configuration bean的显式导入而不是使用ComponenScan?

e.g:

@SpringApplicationConfiguration(classes = TestClass.TestConfiguration.class)
public class TestClass extends AbstractTest {

    @Configuration
    @Import( TestConfig.class )
    public static class TestConfiguration {
       //...
    }
}

@Configuration
@EnableAutoConfiguration(exclude = { EmbeddedMongoAutoConfiguration.class })
// @ComponentScan(value = { "package1", "package2" }) // GET RID OF THIS
@Import( { TestMongoConfiguration.class /*, OtherConfig.class, ... */ } )
public class TestConfig {

}

通过这种方式,您可以控制配置加载方式的顺序。