繁体   English   中英

当我有许多 SpringBootTest 类时,如何有效地使用嵌套配置类来注入依赖项

[英]How can I efficiently use nested configuration classes to inject dependencies when I have many SpringBootTest classes

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

公认的答案是在每个@SpringBootTest测试夹具 class 中定义一个嵌套的 class,用@TestConfiguration对其进行注释,并在其中为每个需要解析的 bean 定义一个工厂方法。 嵌套类的影响范围仅限于测试夹具,影响夹具中的所有测试,但不影响其他夹具中定义的测试。

当在每个测试夹具中运行测试时,这提供了对注入到组件中的依赖项的细粒度控制。

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

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

我需要一种更具可扩展性的方式来做到这一点。 例如,在上面的例子中,我希望能够定义两个配置类,一个用于测试夹具使用的两个配置中的每一个。 然后我希望能够为每个测试夹具指定应该使用两个配置类中的哪一个。 我努力了:

  1. 我尝试使用@Import注释将一个文本夹具的嵌套配置 class 导入另一个测试夹具,但是这样做时,配置 class 在后者中被忽略。
  2. 我还尝试将嵌套配置 class 移动到上层,以便它可以用于所有未明确定义不同的测试夹具作为嵌套 class,但在这种情况下,配置 ZA2F2ED4F8EBC2CBB4C21A29DC40AB1 测试夹具全部忽略。

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

经过一些实验,我达到了以下解决方案。 我将添加所有细节来总结我在上一个问题中学到的东西。

背景

  1. 我们有两个接口: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 注释接口及其实现
  2. 在 main/java 下实现配置 class 如下:

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

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

  1. 在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();
    }
}

  1. 如下注释 InterfaceTests 测试夹具:
@RunWith(SpringRunner.class)
@SpringBootTest(classes = {AllMockInjector.class})
public class InterfaceTests { ... }
  1. 如下注释 ClientTests 测试夹具:
@RunWith(SpringRunner.class)
@SpringBootTest(classes = {MockServerInjector.class})
public class ClientTests { ... }
  1. 如下注释 ServerTests 测试夹具:
@RunWith(SpringRunner.class)
@SpringBootTest(classes = {MockClientInjector.class})
public class ServerTests { ... }
  1. 如下注释 IntegrationTests 测试夹具:
@RunWith(SpringRunner.class)
@SpringBootTest(classes = {RealInjector.class})
public class IntegrationTests { ... }

最后

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

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 中添加它。

概括

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM