在测试中使用Spring @ConfigurationProperties读取Map

时间:2018-02-06 13:09:20

标签: java spring junit

根据Spring Boot integration tests doesn't read properties files的建议,我创建了以下代码,目的是从JUnit测试中的属性中读取地图。 (我正在使用yml格式,并使用@ConfigurationProperties而不是@Value)

@RunWith(SpringJUnit4ClassRunner.class)
@TestPropertySource(locations="classpath:application-test.yml")
@ContextConfiguration(classes = {PropertiesTest.ConfigurationClass.class, PropertiesTest.ClassToTest.class})
public class PropertiesTest {

    @Configuration
    @EnableConfigurationProperties
    static class ConfigurationClass {
    }


    @ConfigurationProperties
    static class ClassToTest {
        private String test;

        private Map<String, Object> myMap = new HashMap<>();

        public String getTest() {
            return test;
        }

        public void setTest(String test) {
            this.test = test;
        }

        public Map<String, Object> getMyMap() {
            return myMap;
        }

    }

    @Autowired
    private ClassToTest config;


    @Test
    public void testStringConfig() {
        Assert.assertEquals(config.test, "works!");
    }

    @Test
    public void testMapConfig() {
        Assert.assertEquals(config.myMap.size(), 1);
    }

}

我的测试配置(在application-test.yml中):

test: works!
myMap:
  aKey: aVal
  aKey2: aVal2

奇怪的是,字符串“有效!”已成功从配置文件中读取,但未填充地图。

我错过了什么?

注意:添加地图设定器会导致以下异常:

Caused by: org.springframework.validation.BindException: org.springframework.boot.bind.RelaxedDataBinder$RelaxedBeanPropertyBindingResult: 1 errors
Field error in object 'target' on field 'myMap': rejected value []; codes [typeMismatch.target.myMap,typeMismatch.myMap,typeMismatch.java.util.Map,typeMismatch]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [target.myMap,myMap]; arguments []; default message [myMap]]; default message [Failed to convert property value of type 'java.lang.String' to required type 'java.util.Map' for property 'myMap'; nested exception is java.lang.IllegalStateException: Cannot convert value of type 'java.lang.String' to required type 'java.util.Map' for property 'myMap': no matching editors or conversion strategy found]
    at org.springframework.boot.bind.PropertiesConfigurationFactory.checkForBindingErrors(PropertiesConfigurationFactory.java:359)
    at org.springframework.boot.bind.PropertiesConfigurationFactory.doBindPropertiesToTarget(PropertiesConfigurationFactory.java:276)
    at org.springframework.boot.bind.PropertiesConfigurationFactory.bindPropertiesToTarget(PropertiesConfigurationFactory.java:240)
    at org.springframework.boot.context.properties.ConfigurationPropertiesBindingPostProcessor.postProcessBeforeInitialization(ConfigurationPropertiesBindingPostProcessor.java:330)
    ... 42 more

1 个答案:

答案 0 :(得分:1)

用调试器度过了一段美好时光后, 我相信这是TestPropertySourceUtils.addPropertiesFilesToEnvironment()中的错误/缺失功能:

try {
    for (String location : locations) {
        String resolvedLocation = environment.resolveRequiredPlaceholders(location);
        Resource resource = resourceLoader.getResource(resolvedLocation);
        environment.getPropertySources().addFirst(new ResourcePropertySource(resource));
    }
}

ResourcePropertySource只能处理.properties个文件,而不能处理.yml。 在常规应用中,YamlPropertySourceLoader已注册,可以处理.yml

作为说明: TestPropertySourceUtils.addPropertiesFilesToEnvironment()被称为:

org.springframework.test.context.support.DelegatingSmartContextLoader.prepareContext()

(继承自AbstractContextLoader

如果DelegatingSmartContextLoader中未指定加载器,则

@ContextConfiguration是您收到的默认上下文加载器。 (实际上@ContextConfiguration指定了一个接口,但AbstractTestContextBootstrapper.resolveContextLoader()将其更改为具体类

要解决此问题,我将配置更改为application-test.properties 并在我的测试中使用该文件。

test=works!
myMap.aKey: aVal

另一条评论:不需要地图上的setter:

https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-external-config.html#boot-features-external-config-loading-yaml

  

使用Spring DataBinder实用程序绑定到类似的属性   (这是@ConfigurationProperties所做的)你需要有一个   java.util.List(或Set)类型的目标bean中的属性和您   或者需要提供一个setter,或者用一个mutable初始化它   价值,例如这将绑定到上面的属性