[英]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:我努力了:
@Import
annotation into the latter, but when doing so, the configuration class is ignored in the latter.我尝试使用@Import
注释将一个文本夹具的嵌套配置 class 导入另一个测试夹具,但是这样做时,配置 class 在后者中被忽略。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.我将添加所有细节来总结我在上一个问题中学到的东西。
Test Fixtures (annotated with @SpringBootTest in test/java)测试夹具(在 test/java 中用 @SpringBootTest 注释)
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.从上面可以清楚地看出,模拟/真实客户端/服务器有四种组合,并且在代码的某些区域需要每种组合。
This solution makes use of the @Configuration and @TestConfiguration annotations in order to implement these requirements with no code duplication.此解决方案利用 @Configuration 和 @TestConfiguration 注释来实现这些要求,而无需重复代码。
@Configuration
public class RealInjector {
@Bean
public IServer createServer(){
return new RealServer();
}
@Bean
public IClient createClient(){
return new RealClient();
}
}
@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();
}
}
@RunWith(SpringRunner.class)
@SpringBootTest(classes = {AllMockInjector.class})
public class InterfaceTests { ... }
@RunWith(SpringRunner.class)
@SpringBootTest(classes = {MockServerInjector.class})
public class ClientTests { ... }
@RunWith(SpringRunner.class)
@SpringBootTest(classes = {MockClientInjector.class})
public class ServerTests { ... }
@RunWith(SpringRunner.class)
@SpringBootTest(classes = {RealInjector.class})
public class IntegrationTests { ... }
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 中添加它。
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.