如何使用@ConfigurationProperties 和@Autowired 测试类
How to test Classes with @ConfigurationProperties and @Autowired
我想测试依赖于 @Autowired
和 @ConfigurationProperties
加载的属性的应用程序的一小部分。我正在寻找一种只加载所需属性而不总是加载整个 ApplicationContext
的解决方案。
这里作为简化示例:
@TestPropertySource(locations = "/SettingsTest.properties")
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {TestSettings.class, TestConfiguration.class})
public class SettingsTest {
@Autowired
TestConfiguration config;
@Test
public void testConfig(){
Assert.assertEquals("TEST_PROPERTY", config.settings().getProperty());
}
}
配置Class:
public class TestConfiguration {
@Bean
@ConfigurationProperties(prefix = "test")
public TestSettings settings (){
return new TestSettings();
}
}
设置Class:
public class TestSettings {
private String property;
public String getProperty() {
return property;
}
public void setProperty(String property) {
this.property = property;
}
}
资源文件夹中的属性文件包含条目:
test.property=TEST_PROPERTY
在我当前的设置中,配置不为空,但没有可用字段。
这些字段不是字段的原因应该与我没有使用 Springboot 而是 Spring 这一事实有关。
那么 Spring 获得这个 运行 的引导方式是什么?
编辑:
我想这样做的原因是:我有一个解析文本文件的解析器,使用的正则表达式存储在一个属性文件中。
为了对此进行测试,我只想加载此解析器所需的属性,这些属性位于 TestSettings.
上方的示例中
在阅读评论时,我已经注意到这不再是单元测试了。然而,使用完整的 Spring 启动配置来进行这个小测试对我来说似乎有点过分了。这就是为什么我问是否有可能只加载具有属性的 class。
几点:
你的主包中不需要 "TestConfiguration" class,因为它所做的只是配置 "TestSettings" bean。您可以简单地通过注释 TestSettings class 本身来做到这一点。
通常您会使用 @SpringApplicationConfiguration 注释加载测试所需的上下文,传递您的应用程序的名称 class。但是,您说您不想加载整个 ApplicationContext(虽然不清楚原因),因此您需要创建一个特殊的配置 class 来只为测试加载。下面我称它为 "TestConfigurationNew" 以避免与您原来的 TestConfiguration class 混淆。
在Spring引导世界中,所有属性一般都保存在"application.properties"文件中;但可以将它们存储在其他地方。下面,我指定了您建议的 "SettingsTest.properties" 文件。请注意,您可以拥有此文件的两个副本,一个在 main/resources 文件夹中,另一个在 test/resources 文件夹中以供测试。
修改代码如下:
TestSettings.java(在主包中)
@Configuration
@ConfigurationProperties(prefix="test", locations = "classpath:SettingsTest.properties")
public class TestSettings {
private String property;
public String getProperty() {
return property;
}
public void setProperty(String property) {
this.property = property;
}
}
SettingsTest.java(在测试包中)
@TestPropertySource(locations="classpath:SettingsTest.properties")
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = TestConfigurationNew.class)
public class SettingsTest {
@Autowired
TestSettings settings;
@Test
public void testConfig(){
Assert.assertEquals("TEST_PROPERTY", settings.getProperty());
}
}
TestConfigurationNew.java(在测试包中):
@EnableAutoConfiguration
@ComponentScan(basePackages = { "my.package.main" })
@Configuration
public class TestConfigurationNew {
}
现在应该可以按照您想要的方式工作了。
您需要使用 @EnableConfigurationProperties
注释您的 TestConfiguraion,如下所示:
@EnableConfigurationProperties
public class TestConfiguration {
@Bean
@ConfigurationProperties(prefix = "test")
public TestSettings settings (){
return new TestSettings();
}
}
此外,您 SettingsTest
中的 @ContextConfiguration
只需要包含 TestConfiguration.class
class:
@TestPropertySource(locations = "/SettingsTest.properties")
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = TestConfiguration.class)
public class SettingsTest {
...
您实际上可以直接将@EnableConfigurationProperties 添加到您的@SpringBootTest。
例如:
@ActiveProfiles("test")
@RunWith(SpringRunner.class)
@SpringBootTest(classes = TestConfiguration.class)
@EnableConfigurationProperties
...
如果你使用Spring启动,现在你只需要:
@RunWith(SpringRunner.class)
@SpringBootTest
没有额外的 @ContextConfiguration
,没有额外的 class 仅用于 EnableAutoConfiguration
和 EnableConfigurationProperties
的测试。您不必指定要加载的配置class,它们都会被加载。
但是,请确保您要在 main/resources/application.yml
中阅读的属性条目也存在于 test/resources/application.yml
中。重复是不可避免的。
另一种方式是:
- 定义 class 仅用于测试的配置,以及
MyApplicationTest.java
在同一级别。这个class可以为空
喜欢:
@EnableAutoConfiguration
@EnableConfigurationProperties(value = {
ConnectionPoolConfig.class
})
public class MyApplicationTestConfiguration {
}
- 并且,在class加载自动装配的配置。
喜欢:
@RunWith(SpringRunner.class)
//@SpringBootTest // the first, easy way
@ContextConfiguration(classes = MyApplicationTestConfiguration.class,
initializers = ConfigFileApplicationContextInitializer.class)
public class ConnectionPoolConfigTest {
@Autowired
private ConnectionPoolConfig config;
基本上,你:
- 对
@EnableConfigurationProperties
和 @EnableAutoConfiguration
使用特定配置,列出您要加载的所有 @ConfigurationProperties
文件
- 在测试 class 中,你加载这个测试配置文件,使用 Spring 定义的初始化器 class 来加载
application.yml
文件。
然后,将要加载的值放入 test/resources/application.yml
。重复是不可避免的。如果您需要加载另一个文件,请使用 @TestProperties()
和一个位置。 注意:@TestProperties
仅支持.properties
个文件。
两种方式都适用于配置 class 加载值
- 来自
application.yml
/application.properties
- 或来自
PropertySource
指定的另一个属性文件,如 @PropertySource(value = "classpath:threadpool.properties")
重要
Spring 文档的最后注释,根据 here
Some people use Project Lombok to add getters and setters automatically. Make sure that Lombok does not generate any particular constructor for such a type, as it is used automatically by the container to instantiate the object.
Finally, only standard Java Bean properties are considered and binding on static properties is not supported.
这意味着,如果你有 lombok.@Builder
而没有 @NoArgsConstructor
或 @AllArgsConstructor
,属性注入将不会发生,因为它只看到不可见的@Builder
创建的构造函数。所以,一定要使用none,或者所有这些注解!
单元测试
为了避免必须加载 Spring 上下文,我们可以使用 Binder
class,它也是 used internally by Spring。
// A map of my properties.
Map<String, String> properties = new HashMap<>();
properties.put("my-prefix.first-property", "foo");
properties.put("my-prefix.second-property", "bar");
// Creates a source backed by my map, you can chose another type of source as needed.
ConfigurationPropertySource source = new MapConfigurationPropertySource(properties)
// Binds my properties to a class that maps them.
Binder binder = new Binder(source);
BindResult<MyConfiguration> result = binder.bind("my-prefix", MyConfiguration.class);
// Should return true if bound successfully.
Assertions.assertTrue(result.isBound);
// Asserts configuration values.
MyConfiguration config = result.get();
Assertions.assertEquals("foo", config.getFirstProperty());
Assertions.assertEquals("bar", config.getSecondProperty());
我想测试依赖于 @Autowired
和 @ConfigurationProperties
加载的属性的应用程序的一小部分。我正在寻找一种只加载所需属性而不总是加载整个 ApplicationContext
的解决方案。
这里作为简化示例:
@TestPropertySource(locations = "/SettingsTest.properties")
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {TestSettings.class, TestConfiguration.class})
public class SettingsTest {
@Autowired
TestConfiguration config;
@Test
public void testConfig(){
Assert.assertEquals("TEST_PROPERTY", config.settings().getProperty());
}
}
配置Class:
public class TestConfiguration {
@Bean
@ConfigurationProperties(prefix = "test")
public TestSettings settings (){
return new TestSettings();
}
}
设置Class:
public class TestSettings {
private String property;
public String getProperty() {
return property;
}
public void setProperty(String property) {
this.property = property;
}
}
资源文件夹中的属性文件包含条目:
test.property=TEST_PROPERTY
在我当前的设置中,配置不为空,但没有可用字段。 这些字段不是字段的原因应该与我没有使用 Springboot 而是 Spring 这一事实有关。 那么 Spring 获得这个 运行 的引导方式是什么?
编辑: 我想这样做的原因是:我有一个解析文本文件的解析器,使用的正则表达式存储在一个属性文件中。 为了对此进行测试,我只想加载此解析器所需的属性,这些属性位于 TestSettings.
上方的示例中在阅读评论时,我已经注意到这不再是单元测试了。然而,使用完整的 Spring 启动配置来进行这个小测试对我来说似乎有点过分了。这就是为什么我问是否有可能只加载具有属性的 class。
几点:
你的主包中不需要 "TestConfiguration" class,因为它所做的只是配置 "TestSettings" bean。您可以简单地通过注释 TestSettings class 本身来做到这一点。
通常您会使用 @SpringApplicationConfiguration 注释加载测试所需的上下文,传递您的应用程序的名称 class。但是,您说您不想加载整个 ApplicationContext(虽然不清楚原因),因此您需要创建一个特殊的配置 class 来只为测试加载。下面我称它为 "TestConfigurationNew" 以避免与您原来的 TestConfiguration class 混淆。
在Spring引导世界中,所有属性一般都保存在"application.properties"文件中;但可以将它们存储在其他地方。下面,我指定了您建议的 "SettingsTest.properties" 文件。请注意,您可以拥有此文件的两个副本,一个在 main/resources 文件夹中,另一个在 test/resources 文件夹中以供测试。
修改代码如下:
TestSettings.java(在主包中)
@Configuration
@ConfigurationProperties(prefix="test", locations = "classpath:SettingsTest.properties")
public class TestSettings {
private String property;
public String getProperty() {
return property;
}
public void setProperty(String property) {
this.property = property;
}
}
SettingsTest.java(在测试包中)
@TestPropertySource(locations="classpath:SettingsTest.properties")
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = TestConfigurationNew.class)
public class SettingsTest {
@Autowired
TestSettings settings;
@Test
public void testConfig(){
Assert.assertEquals("TEST_PROPERTY", settings.getProperty());
}
}
TestConfigurationNew.java(在测试包中):
@EnableAutoConfiguration
@ComponentScan(basePackages = { "my.package.main" })
@Configuration
public class TestConfigurationNew {
}
现在应该可以按照您想要的方式工作了。
您需要使用 @EnableConfigurationProperties
注释您的 TestConfiguraion,如下所示:
@EnableConfigurationProperties
public class TestConfiguration {
@Bean
@ConfigurationProperties(prefix = "test")
public TestSettings settings (){
return new TestSettings();
}
}
此外,您 SettingsTest
中的 @ContextConfiguration
只需要包含 TestConfiguration.class
class:
@TestPropertySource(locations = "/SettingsTest.properties")
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = TestConfiguration.class)
public class SettingsTest {
...
您实际上可以直接将@EnableConfigurationProperties 添加到您的@SpringBootTest。
例如:
@ActiveProfiles("test")
@RunWith(SpringRunner.class)
@SpringBootTest(classes = TestConfiguration.class)
@EnableConfigurationProperties
...
如果你使用Spring启动,现在你只需要:
@RunWith(SpringRunner.class)
@SpringBootTest
没有额外的 @ContextConfiguration
,没有额外的 class 仅用于 EnableAutoConfiguration
和 EnableConfigurationProperties
的测试。您不必指定要加载的配置class,它们都会被加载。
但是,请确保您要在 main/resources/application.yml
中阅读的属性条目也存在于 test/resources/application.yml
中。重复是不可避免的。
另一种方式是:
- 定义 class 仅用于测试的配置,以及
MyApplicationTest.java
在同一级别。这个class可以为空
喜欢:
@EnableAutoConfiguration
@EnableConfigurationProperties(value = {
ConnectionPoolConfig.class
})
public class MyApplicationTestConfiguration {
}
- 并且,在class加载自动装配的配置。
喜欢:
@RunWith(SpringRunner.class)
//@SpringBootTest // the first, easy way
@ContextConfiguration(classes = MyApplicationTestConfiguration.class,
initializers = ConfigFileApplicationContextInitializer.class)
public class ConnectionPoolConfigTest {
@Autowired
private ConnectionPoolConfig config;
基本上,你:
- 对
@EnableConfigurationProperties
和@EnableAutoConfiguration
使用特定配置,列出您要加载的所有@ConfigurationProperties
文件 - 在测试 class 中,你加载这个测试配置文件,使用 Spring 定义的初始化器 class 来加载
application.yml
文件。
然后,将要加载的值放入 test/resources/application.yml
。重复是不可避免的。如果您需要加载另一个文件,请使用 @TestProperties()
和一个位置。 注意:@TestProperties
仅支持.properties
个文件。
两种方式都适用于配置 class 加载值
- 来自
application.yml
/application.properties
- 或来自
PropertySource
指定的另一个属性文件,如@PropertySource(value = "classpath:threadpool.properties")
重要
Spring 文档的最后注释,根据 here
Some people use Project Lombok to add getters and setters automatically. Make sure that Lombok does not generate any particular constructor for such a type, as it is used automatically by the container to instantiate the object.
Finally, only standard Java Bean properties are considered and binding on static properties is not supported.
这意味着,如果你有 lombok.@Builder
而没有 @NoArgsConstructor
或 @AllArgsConstructor
,属性注入将不会发生,因为它只看到不可见的@Builder
创建的构造函数。所以,一定要使用none,或者所有这些注解!
单元测试
为了避免必须加载 Spring 上下文,我们可以使用 Binder
class,它也是 used internally by Spring。
// A map of my properties.
Map<String, String> properties = new HashMap<>();
properties.put("my-prefix.first-property", "foo");
properties.put("my-prefix.second-property", "bar");
// Creates a source backed by my map, you can chose another type of source as needed.
ConfigurationPropertySource source = new MapConfigurationPropertySource(properties)
// Binds my properties to a class that maps them.
Binder binder = new Binder(source);
BindResult<MyConfiguration> result = binder.bind("my-prefix", MyConfiguration.class);
// Should return true if bound successfully.
Assertions.assertTrue(result.isBound);
// Asserts configuration values.
MyConfiguration config = result.get();
Assertions.assertEquals("foo", config.getFirstProperty());
Assertions.assertEquals("bar", config.getSecondProperty());