java - 当我有很多 SpringBootTest 类时,如何有效地使用嵌套配置类来注入(inject)依赖项

标签 java spring-boot dependency-injection spring-boot-test

这个问题是Can I use code to control the dependency resolution decisions made by ApplicationContext in Spring Boot?的续集

可接受的答案是在每个 @SpringBootTest 测试装置类中定义一个嵌套类,用 @TestConfiguration 对其进行注释,并在其中为每个测试装置定义一个工厂方法需要解决的bean。嵌套类的影响范围仅限于测试装置,影响该装置中的所有测试,但不影响其他装置中定义的测试。

这可以在每个测试装置中运行测试时对注入(inject)组件的依赖项进行细粒度控制。

这种方法的问题是它需要在每个测试装置类中添加一个嵌套的解析器类。 这是不可扩展的。 考虑一个具有 10 个测试装置的项目。其中 9 个使用相同的注入(inject)依赖项,只有第 10 个仅需要一个特定接口(interface)的不同实现。

在这种情况下,我需要将测试配置类复制到 9 个测试夹具类中,并仅在第 10 个测试中使用第二个配置类。

我需要一种更具可扩展性的方法来做到这一点。例如,在上面的情况下,我希望能够定义两个配置类,一个用于测试装置使用的两种配置中的每一种。然后我希望能够为每个测试装置指定应使用两个配置类中的哪一个。 我尝试过:

  1. 我尝试导入一个文本的嵌套配置类 使用 @Import 注释将固定装置导入到另一个测试固定装置中 后者,但是这样做时,配置类会被忽略 后者。
  2. 我还尝试将嵌套配置类移动到 上层,以便它可以用于所有测试装置 没有明确定义另一个不同的类作为嵌套类,但是在这个 如果配置类被所有测试装置忽略。

总而言之,我正在寻找一种有效的方法,该方法允许我只编写每个配置类一次,然后有选择地将一个配置类应用于每个 SpringBootTest 类,而无需复制它。

最佳答案

经过一些实验,我得到了以下解决方案。 我将添加所有详细信息,总结我在 the previous question 中学到的知识。也是。

背景

  1. 我们有两个接口(interface):IClient 和 IServer
  2. IClient 有两种实现:RealClient 和 MockClient。
  3. IServer 有两种实现:RealServer 和 MockServer。

要求

  1. 生产代码(在 main/java 中)应使用两者的 Real 实现。
  2. 测试装置(在 test/java 中使用 @SpringBootTest 注解)

    • InterfaceTests 定义应使用 MockServer 和 MockClient 的测试
    • ClientTests 定义应使用 MockServer 和 RealClient 来测试 RealClient 的测试。
    • ServerTests 定义了应使用 MockClient 和 RealServer 来测试 RealServer 的测试。
    • IntegrationTests 定义应使用 RealServer 和 RealClient 的测试

从上面可以清楚地看出,模拟/真实客户端/服务器有四种组合,并且每种组合在代码的某些区域都需要。

解决方案

此解决方案利用 @Configuration 和 @TestConfiguration 注释来实现这些要求,而无需重复代码。

  1. 不要使用 @Component 注释接口(interface)及其实现
  2. 在main/java下实现一个配置类,如下:

@Configuration
public class RealInjector {
    @Bean
    public IServer createServer(){
        return new RealServer();
    }

    @Bean
    public IClient createClient(){
        return new RealClient();
    }
}

  • 在test/java下实现这三个测试配置类
  • @TestConfiguration
    public class AllMockInjector {
        @Bean
        public IServer createServer(){
            return new MockServer();
        }
    
        @Bean
        public IClient createClient(){
            return new MockClient();
        }
    }
    
    @TestConfiguration
    public class MockServerInjector{
        @Bean
        public IServer createServer(){
            return new MockServer();
        }
    
        @Bean
        public IClient createClient(){
            return new RealClient();
        }
    }
    
    @TestConfiguration
    public class MockClientInjector{
        @Bean
        public IServer createServer(){
            return new RealServer();
        }
    
        @Bean
        public IClient createClient(){
            return new MockClient();
        }
    }
    
    
  • 按如下方式注释 InterfaceTests 测试装置:
  • @RunWith(SpringRunner.class)
    @SpringBootTest(classes = {AllMockInjector.class})
    public class InterfaceTests { ... }
    
  • 按如下方式注释 ClientTests 测试装置:
  • @RunWith(SpringRunner.class)
    @SpringBootTest(classes = {MockServerInjector.class})
    public class ClientTests { ... }
    
  • 按如下方式注释 ServerTests 测试装置:
  • @RunWith(SpringRunner.class)
    @SpringBootTest(classes = {MockClientInjector.class})
    public class ServerTests { ... }
    
  • 按如下方式注释 IntegrationTests 测试装置:
  • @RunWith(SpringRunner.class)
    @SpringBootTest(classes = {RealInjector.class})
    public class IntegrationTests { ... }
    

    终于

    为了让测试配置类覆盖 main/java 中的 RealInjector 配置类,我们需要设置属性:

    spring.main.allow-bean-definition-overriding=true 
    

    实现此目的的一种方法是对上述每个测试装置进行注释,如下所示:

    @SpringBootTest(properties = ["spring.main.allow-bean-definition-overriding=true"])
    class TestFixture { ... }
    

    但这非常冗长,特别是如果您有很多测试装置。 相反,您可以在 test/resources 下的 application.properties 文件中添加以下内容:

    spring.main.allow-bean-definition-overriding=true
    

    您可能还需要将其添加到 main/resources 下的 application.properties 中。

    摘要

    此解决方案使您可以对注入(inject)到生产和测试代码中的实现进行细粒度控制。该解决方案不需要重复代码或外部配置文件(除了 test/resources/application.properties 中的一个属性)。

    关于java - 当我有很多 SpringBootTest 类时,如何有效地使用嵌套配置类来注入(inject)依赖项,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58583454/

    相关文章:

    java - 与 Java 中单词末尾的子字符串之一匹配的正确正则表达式是什么?

    java - 小程序执行无法执行打包在可执行jar内的批处理文件

    scala - 带 Macwire 的可注入(inject)工厂

    php - 在 Symfony2 ContainerAwareCommand 中获取服务定义

    java - 使用弱引用有什么好处?

    java - 检查互联网连接是否可用 (https)

    java - 为什么 import.sql 在 Spring Boot 中会失败?

    spring-boot - Thymeleaf 解析预处理字符串到日期并格式化它

    java - 应用程序域必须与 Facebook 网络游戏 URL (https)、移动站点 URL、Unity 二进制 URL、站点 URL 或安全页面选项卡 URL 的域相匹配

    .net - 我应该使用哪种依赖注入(inject)工具?