使用YAML的Spring @PropertySource

che*_*tts 89 spring spring-boot

Spring Boot允许我们用YAML等价物替换我们的application.properties文件.但是我的测试似乎遇到了麻烦.如果我注释我的TestConfiguration(一个简单的Java配置),它期望一个属性文件.

例如,这不起作用: @PropertySource(value = "classpath:application-test.yml")

如果我在我的YAML文件中有这个:

db:
  url: jdbc:oracle:thin:@pathToMyDb
  username: someUser
  password: fakePassword
Run Code Online (Sandbox Code Playgroud)

我会用这样的东西来利用这些价值观:

@Value("${db.username}") String username
Run Code Online (Sandbox Code Playgroud)

但是,我最终得到了错误:

Could not resolve placeholder 'db.username' in string value "${db.username}"
Run Code Online (Sandbox Code Playgroud)

我如何在测试中利用YAML的优点?

bal*_*usm 51

因为它提到@PropertySource不加载yaml文件.作为解决方法,您可以自己加载文件并将加载的属性添加到Environment.

实施ApplicationContextInitializer:

public class YamlFileApplicationContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
  @Override
  public void initialize(ConfigurableApplicationContext applicationContext) {
    try {
        Resource resource = applicationContext.getResource("classpath:file.yml");
        YamlPropertySourceLoader sourceLoader = new YamlPropertySourceLoader();
        PropertySource<?> yamlTestProperties = sourceLoader.load("yamlTestProperties", resource, null);
        applicationContext.getEnvironment().getPropertySources().addFirst(yamlTestProperties);
    } catch (IOException e) {
        throw new RuntimeException(e);
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

将初始化程序添加到测试中:

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = Application.class, initializers = YamlFileApplicationContextInitializer.class)
public class SimpleTest {
  @Test
  public test(){
    // test your properties
  }
}
Run Code Online (Sandbox Code Playgroud)


Ola*_*ell 44

Spring-boot有一个帮助,只需添加

@ContextConfiguration(initializers = ConfigFileApplicationContextInitializer.class)
Run Code Online (Sandbox Code Playgroud)

在测试类的顶部或抽象测试超类.

  • 这对我来说不起作用,属性没有加载. (7认同)
  • 不要忘记PropertySourcesPlaceholderConfigurer (2认同)

Dav*_*yer 26

@PropertySource只支持属性文件(这是Spring的限制,而不是Boot本身).欢迎在JIRA开设特色要求票.

  • 我想你可以写一个`ApplicationContextInitializer`并将它添加到测试配置中(只需使用`YamlPropertySourceLoader`来增强`Environment`).我个人更喜欢它,如果`@ PropertySource`本身支持这种行为. (10认同)
  • 是的,仍然不支持它:https://jira.spring.io/browse/SPR-13912 (3认同)

小智 20

@PropertySource可以通过factory参数配置.所以你可以这样做:

@PropertySource(value = "classpath:application-test.yml", factory = YamlPropertyLoaderFactory.class)
Run Code Online (Sandbox Code Playgroud)

YamlPropertyLoaderFactory您的自定义属性加载器在哪里:

public class YamlPropertyLoaderFactory extends DefaultPropertySourceFactory {
    @Override
    public PropertySource<?> createPropertySource(String name, EncodedResource resource) throws IOException {
        if (resource == null){
            return super.createPropertySource(name, resource);
        }

        return new YamlPropertySourceLoader().load(resource.getResource().getFilename(), resource.getResource(), null);
    }
}
Run Code Online (Sandbox Code Playgroud)

灵感来自/sf/answers/3211771321/

  • 对我来说,它需要做一点修改,如下所示:`CompositePropertySource propertySource = new CompositePropertySource(name); new YamlPropertySourceLoader().load(resource.getResource().getFilename(), resource.getResource()).stream().forEach(propertySource::addPropertySource); 返回属性源;` (7认同)
  • 当文件不存在而不是正确的“FileNotFoundException”时,这个底层 yaml 解析会抛出一个“IllegalStateException”——所以为了让它与“@PropertySource(..., ignoreResourceNotFound = true)”一起工作,你需要捕获并处理这种情况:`try { return new YamlPropertySourceLoader().load(resource.getResource().getFilename(), resource.getResource(), null); } catch (IllegalStateException e) { throw (IOException) e.getCause(); }` (2认同)
  • 如果您需要获取特定配置文件的属性,YamlPropertySourceLoader.load() 中的第三个参数是配置文件名称。YamlPropertySourceLoader.load() 已更改为返回列表而不是单个属性源。这是更多信息 /sf/answers/3758828601/ (2认同)
  • 这是迄今为止最干净的方法。 (2认同)

小智 19

从Spring Boot 1.4开始,您可以使用新的@SpringBootTest注释通过使用Spring Boot支持引导集成测试来更轻松地实现这一目标(并简化集成测试设置).

关于Spring博客的详细信息.

据我所知,这意味着您可以像生产代码一样获得Spring Boot 外部化配置优势的所有好处,包括从类路径中自动获取YAML配置.

默认情况下,此注释将

...首先尝试@Configuration从任何内部类加载,如果失败,它将搜索您的主@SpringBootApplication类.

但是如果需要,您可以指定其他配置类.

对于这种特殊情况,你可以结合@SpringBootTest使用@ActiveProfiles( "test" ),Spring会选择你的YAML配置,前提是它遵循正常的引导命名标准(即application-test.yml).

@RunWith( SpringRunner.class )
@SpringBootTest
@ActiveProfiles( "test" )
public class SpringBootITest {

    @Value("${db.username}")
    private String username;

    @Autowired
    private MyBean myBean;

    ...

}
Run Code Online (Sandbox Code Playgroud)

注意:SpringRunner.class是新名称SpringJUnit4ClassRunner.class


Pom*_*pot 15

另一个选择是设置spring.config.location通过@TestPropertySource:

@TestPropertySource(properties = { "spring.config.location = classpath:<path-to-your-yml-file>" }
Run Code Online (Sandbox Code Playgroud)

  • 我已经通过以下行对输入进行了参数化设置:`@TestPropertySource(properties = {“ spring.config.location = classpath:application-$ {test.env} .yml”})`IMO您的IMO无疑是最好的答案。 (3认同)
  • 好主意,测试非常简单,非常感谢!只是补充一下,可以包含多个配置文件,如下: `@TestPropertySource(properties = {"spring.config.location=classpath:application-config.yml,classpath:test-config.yml,..." })` (3认同)

Bij*_*men 10

加载yaml属性的方法,恕我直言可以通过两种方式完成:

一个.您可以将配置放在标准位置 - application.yml通常在类路径根目录中 - src/main/resources并且此yaml属性应该由Spring引导自动加载,并使用您提到的扁平路径名.

湾 第二种方法更广泛,基本上定义一个类以这种方式保存您的属性:

@ConfigurationProperties(path="classpath:/appprops.yml", name="db")
public class DbProperties {
    private String url;
    private String username;
    private String password;
...
}
Run Code Online (Sandbox Code Playgroud)

所以基本上这就是说加载yaml文件并根据"db"的根元素填充DbProperties类.

现在要在任何类中使用它,你将不得不这样做:

@EnableConfigurationProperties(DbProperties.class)
public class PropertiesUsingService {

    @Autowired private DbProperties dbProperties;

}
Run Code Online (Sandbox Code Playgroud)

这些方法中的任何一种都应该使用Spring-boot干净利落地工作.

  • 这些天(虽然当时没有提出这个问题),`snakeyaml`被`spring-boot-starter`作为传递依赖拉入,所以不需要将它添加到你的`pom.xml`或`build.gradle`,除非你有一个根深蒂固的冲动使用不同的版本.:) (3认同)
  • 它现在是`locations`,而不是`path`,并且还需要`ConfigFileApplicationContextInitializer`. (2认同)

小智 7

从 Spring Boot 2.4.0 开始,您可以使用ConfigDataApplicationContextInitializer,如下所示:

@SpringJUnitConfig(
    classes = { UserAccountPropertiesTest.TestConfig.class },
    initializers = { ConfigDataApplicationContextInitializer.class }
)
class UserAccountPropertiesTest {

    @Configuration
    @EnableConfigurationProperties(UserAccountProperties.class)
    static class TestConfig { }

    @Autowired
    UserAccountProperties userAccountProperties;

    @Test
    void getAccessTokenExpireIn() {
       assertThat(userAccountProperties.getAccessTokenExpireIn()).isEqualTo(120);
    }

    @Test
    void getRefreshTokenExpireIn() {
        assertThat(userAccountProperties.getRefreshTokenExpireIn()).isEqualTo(604800);
    }
}
Run Code Online (Sandbox Code Playgroud)

另请参阅: https: //www.baeldung.com/spring-boot-testing-configurationproperties#YAML-binding