【问题标题】:Spring boot test with multiple configuration具有多个配置的 Spring Boot 测试
【发布时间】:2018-12-08 17:35:08
【问题描述】:

在我的 Spring boot 2.1 项目中,我有不同的 @Configurations 用于不同的测试(ConfigurationA 和 ConfigurationB),它们位于不同的包中。两种配置都定义了相同的一组 bean,但方式不同(模拟与真实)

由于我知道 Spring Boot 2.1 中引入了 Bean overriding 机制,因此我设置了属性:spring.main.allow-bean-definition-overriding=true

但是,我确实对以下配置和测试类的设置进行了测试。首先在生产部分有一个@Configuration(我使用的是Maven):

package com.stackoverflow;

@Configuration
public class ProdConfiguration{
...
}

那么在测试分支中就有一个通用的Test@Configuration在同一个包级别:

package com.stackoverflow

@Configuration
public class TestConfiguration {
  @Bean
  public GameMap gameMap() {
    return Mockito.mock(GameMap.class);
  }
}

在一个子包中我还有另一个@Configuration

package com.stackoverflow.impl;

@Configuration
public class RealMapTestConfiguration {
  @Bean
  public GameMap gameMap() {
    return new GameMap("testMap.json");
  }
}

当然还有困扰我的测试:

package com.stackoverflow.impl;

@ExtendWith(SpringExtension.class)
@SpringBootTest
@ContextConfiguration(classes={RealMapTestConfiguration.class, ProdConfiguration.class})
@ActiveProfiles("bug") // spring.main.allow-bean-definition-overriding=true
public class MapImageServiceIT {
  @Autowired
  private GameMap map;
} 

事实证明,注入到我的测试中的GameMap 是来自TestConfiguration 的模拟实例,而不是来自RealMapTestConfiguration 的真实实例。 Aparrently 在我的测试中,当我想要ProdConfigurationRealMapTestConfiguration 时,我有来自ProdConfigurationTestConfiguration 的配置。由于ProdConfiguration*TestConfiguration 中定义的bean 不同,因此组合有效,但TestConfigurationRealMapTestConfiguration 定义相同。似乎TestConfiguration 被组件扫描拾取,因为它与ProdConfiguration 在同一个包中。 我的印象是,在覆盖 bean 时,更接近测试类的 bean 定义将是首选。然而,情况似乎并非如此。

所以这是我的问题:

  1. 覆盖 bean 时,顺序是什么?哪个 bean 覆盖了哪个?
  2. 如何在我的测试中获取正确的实例(使用不同的 bean 名称不是一个选项,因为实际上注入的 bean 不是直接在测试中使用,而是在测试使用的服务中,并且没有限定符。)

【问题讨论】:

  • 您是否尝试过使用专用的集成测试配置文件?另外,既然你从配置中返回的是一个模拟,为什么不在你的测试类中使用@MockBean
  • @DoeJohnson:以MockBean 的身份注入bean 解决了问题,因为我可以在设置中定义模拟行为并且只使用一个TestConfiguration

标签: java spring-boot testing dependency-injection integration-testing


【解决方案1】:

我没有使用过spring.main.allow-bean-definition-overriding=true 属性,但是在测试类中指定特定配置对我来说可以很好地作为在不同测试中切换对象的一种方式。

你说...

事实证明,注入到我的测试中的 GameMap 是来自 TestConfiguration 的模拟实例,而不是来自 RealMapTestConfiguration 的真实实例。

但 RealMapTestConfiguration 确实返回一个模拟

package com.stackoverflow.impl;

@Configuration
public class RealMapTestConfiguration {
  @Bean
  public GameMap gameMap() {
    return Mockito.mock(GameMap.class);
  }
}

【讨论】:

  • 代码示例弄错了:TestConfiguration 返回一个模拟实例,RealMapTestConfiguration 返回一个真实实例。
【解决方案2】:

我认为这里的问题是包含ContextConfiguration 会使@SpringBootTest 的(部分)效果无效。 @SpringBootTest 具有在您的应用程序中查找@SpringBootConfiguration 的效果(我相信从同一个包开始)。但是,如果应用了ContextConfiguration,则会从那里加载配置。

另一种说法:因为您的测试中有ContextConfiguration,所以扫描@Configuration 类被禁用,TestConfiguration 未被加载。

我不认为我对您的配置设置有完整的了解,因此不能在这里真正推荐最佳做法,但解决此问题的快速方法是在您的测试中将TestConfiguration 添加到您的ContextConfiguration。确保最后添加它,以便它覆盖其他两个配置中的 bean 定义。

可能起作用的另一件事是完全删除 @ContextConfiguration 并让 SpringBootApplication 扫描完成它的工作 - 这就是你所说的最接近的 bean 定义可能适用的地方。

【讨论】:

  • 澄清问题。将两个配置文件放在@SpringBootTest 上并删除@ContextConfiguration 没有区别,因为TestConfiguration 被包扫描拾取并且不应包含该配置。
  • 感谢您的澄清。我相信使用@SpringBootTest 指定类与使用ContextConfiguration 具有相同的效果——您可以在您的application.properties 中设置debug=true 以查看Spring 使用的配置。无论哪种方式,听起来@MockBean 也可以做你想做的事。
【解决方案3】:

在这种情况下,不要在配置类上使用@Configuration,而是使用@Import 手动将其导入到测试中,例如:

@SpringBootTest
@Import(MyTest.MyTestConfig.class)
public class MyTest {

    @Autowired
    private String string;

    @Test
    public void myTest() {
        System.out.println(string);
    }

    static class MyTestConfig {
        @Bean
        public String string() {
            return "String";
        }
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-02-16
    • 1970-01-01
    • 1970-01-01
    • 2018-02-26
    • 2018-08-31
    • 2020-09-18
    • 2017-12-25
    相关资源
    最近更新 更多