简体   繁体   English

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

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

This question is a sequel to Can I use code to control the dependency resolution decisions made by ApplicationContext in Spring Boot?这个问题是Can I use code to control the dependency resolution Decisions made by ApplicationContext in Spring Boot?

The accepted answer is to define a nested class within each @SpringBootTest test fixture class, to annotate it with @TestConfiguration and to define within it a factory method for each bean that needs to be resolved.公认的答案是在每个@SpringBootTest测试夹具 class 中定义一个嵌套的 class,用@TestConfiguration对其进行注释,并在其中为每个需要解析的 bean 定义一个工厂方法。 The influence of the nested classes is scoped to the test fixture affecting all of the tests in the fixture but not affecting tests defined in other fixtures.嵌套类的影响范围仅限于测试夹具,影响夹具中的所有测试,但不影响其他夹具中定义的测试。

This provides fine grained control over the dependencies injected into the components when running the tests in each test fixture.当在每个测试夹具中运行测试时,这提供了对注入到组件中的依赖项的细粒度控制。

The problem with this approach is that it requires adding a nested resolver class within each test fixture class.这种方法的问题在于,它需要在每个测试夹具 class 中添加嵌套解析器 class。 This is not scalable.这是不可扩展的。 Consider a project with 10 test fixtures.考虑一个有 10 个测试装置的项目。 9 of these use the same injected dependencies and only the 10th requires a different implementation for only one particular interface.其中 9 个使用相同的注入依赖项,只有第 10 个需要针对一个特定接口的不同实现。

In this case I would need to copy the test configuration class into 9 test fixture classes and use a second configuration class for only the 10th test.在这种情况下,我需要将测试配置 class 复制到 9 个测试夹具类中,并仅在第 10 次测试中使用第二个配置 class。

I need a more scalable way to do this.我需要一种更具可扩展性的方式来做到这一点。 For instance, in the case above, I would like to be able to define two configuration classes, one for each of the two configurations used by the test fixtures.例如,在上面的例子中,我希望能够定义两个配置类,一个用于测试夹具使用的两个配置中的每一个。 Then I would like to be able specify for each test fixture which of the two configuration classes should be used.然后我希望能够为每个测试夹具指定应该使用两个配置类中的哪一个。 I have tried:我努力了:

  1. I tried importing the nested configuration class of one text fixture into another test fixture using the @Import annotation into the latter, but when doing so, the configuration class is ignored in the latter.我尝试使用@Import注释将一个文本夹具的嵌套配置 class 导入另一个测试夹具,但是这样做时,配置 class 在后者中被忽略。
  2. I also tried moving a nested configuration class to the upper level so that it might be used for all test fixtures that do not explicitly define a different one as a nested class, but in this case the configuration class is ignored by all test fixtures.我还尝试将嵌套配置 class 移动到上层,以便它可以用于所有未明确定义不同的测试夹具作为嵌套 class,但在这种情况下,配置 ZA2F2ED4F8EBC2CBB4C21A29DC40AB1 测试夹具全部忽略。

So in summary I am looking for an efficient way that would allow me to write each configuration class only once and then to selectively apply one to each SpringBootTest class without needing to copy it.因此,总而言之,我正在寻找一种有效的方法,它允许我只编写每个配置 class 一次,然后选择性地将一个配置应用于每个 SpringBootTest class 而无需复制它。

After some experimentation I have reached the following solution.经过一些实验,我达到了以下解决方案。 I will add all the details summarizing what I learned in the previous question too.我将添加所有细节来总结我在上一个问题中学到的东西。

Background背景

  1. We have two interfaces: IClient and IServer我们有两个接口:IClient 和 IServer
  2. There are two implementations of IClient: RealClient and MockClient. IClient 有两种实现方式:RealClient 和 MockClient。
  3. There are two implementations of IServer: RealServer and MockServer. IServer 有两种实现方式:RealServer 和 MockServer。

Requirements要求

  1. Production code (in main/java) should use the Real implementations of both.生产代码(在 main/java 中)应该使用两者的 Real 实现。
  2. Test Fixtures (annotated with @SpringBootTest in test/java)测试夹具(在 test/java 中用 @SpringBootTest 注释)

    • InterfaceTests defines tests that should use a MockServer and a MockClient InterfaceTests 定义了应该使用 MockServer 和 MockClient 的测试
    • ClientTests defines tests that should use a MockServer and RealClient to test the RealClient. ClientTests 定义了应该使用 MockServer 和 RealClient 来测试 RealClient 的测试。
    • ServerTests defines tests that should use a MockClient and a RealServer to test the RealServer. ServerTests 定义了应该使用 MockClient 和 RealServer 来测试 RealServer 的测试。
    • IntegrationTests defines tests that should use a RealServer and a RealClient IntegrationTests 定义了应该使用 RealServer 和 RealClient 的测试

From the above it is clear that there are four combinations of mock/real client/server and each combination is needed in some area of the code.从上面可以清楚地看出,模拟/真实客户端/服务器有四种组合,并且在代码的某些区域需要每种组合。

Solution解决方案

This solution makes use of the @Configuration and @TestConfiguration annotations in order to implement these requirements with no code duplication.此解决方案利用 @Configuration 和 @TestConfiguration 注释来实现这些要求,而无需重复代码。

  1. Do NOT annotate interfaces nor their implementations with @Component不要使用 @Component 注释接口及其实现
  2. Under main/java implement a configuration class as follows:在 main/java 下实现配置 class 如下:

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

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

  1. Under test/java implement these three test configuration classes在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. Annotate the InterfaceTests test fixture as follows:如下注释 InterfaceTests 测试夹具:
@RunWith(SpringRunner.class)
@SpringBootTest(classes = {AllMockInjector.class})
public class InterfaceTests { ... }
  1. Annotate the ClientTests test fixture as follows:如下注释 ClientTests 测试夹具:
@RunWith(SpringRunner.class)
@SpringBootTest(classes = {MockServerInjector.class})
public class ClientTests { ... }
  1. Annotate the ServerTests test fixture as follows:如下注释 ServerTests 测试夹具:
@RunWith(SpringRunner.class)
@SpringBootTest(classes = {MockClientInjector.class})
public class ServerTests { ... }
  1. Annotate the IntegrationTests test fixture as follows:如下注释 IntegrationTests 测试夹具:
@RunWith(SpringRunner.class)
@SpringBootTest(classes = {RealInjector.class})
public class IntegrationTests { ... }

Finally最后

In order for the test configuration classes to override the RealInjector configuration class from main/java we need to set the property:为了让测试配置类覆盖来自 main/java 的 RealInjector 配置 class,我们需要设置属性:

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

One way to do this is to annotate each of the above test fixtures as follows:一种方法是对上述每个测试夹具进行如下注释:

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

but this is quite verbose especially if you have many test fixtures.但这非常冗长,特别是如果您有很多测试装置。 Instead you can add the following in the application.properties file under test/resources:相反,您可以在 test/resources 下的 application.properties 文件中添加以下内容:

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

You may also need to add it in application.properties under main/resources too.您可能还需要在 main/resources 下的 application.properties 中添加它。

Summary概括

This solution gives you fine grained control over the implementations that are injected into your code for production and for tests.此解决方案使您可以对注入到您的代码中以用于生产和测试的实现进行细粒度控制。 The solution requires no code duplication or external configuration files (apart from one property in test/resources/application.properties).该解决方案不需要代码重复或外部配置文件(除了 test/resources/application.properties 中的一个属性)。

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

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