Fail to inject @Value in tests with Spring, Spock & Groovy

Viewed 1047

I have problems with injection @Value('${mybean.secret}') property into my bean during tests in Spock with Spring Boot & Groovy.

I have a very simple test class MyBeanTest

@ContextConfiguration(classes = [
    MyAppConfig
])
@PropertySource([
    "classpath:context/default-properties.yml"
])
class MyBeanTest extends Specification {

    @Autowired
    MyBean myBean

    def "should populate properties "() {
        expect:
            myBean.secretProperty == "iCantTellYou"
    }
}

And MyAppConfig.groovy as this:

@Configuration
class MyAppConfig {

    @Bean
    MyBean credential(@Value('${mybean.secret}') String secret) {
        return new MyBean(secret)
    }
}

When I run tests the value that is injected into secret is simply ${mybean.secret}. The real value is not injected from properties file I enclose on test specification.

I'm using single-quote on @Value because of Groovy. Double quote with $ sign makes it being processed by groovy GString mechanism.

However, the problem doesn't occur on regular application run.

If I start application and put the breakpoint on the MyAppConfig#credential method the secret value is correctly read from the properties file, which are configured as follow:

@Configuration
@PropertySource(["classpath:context/default-properties.yml"])
class PropertiesConfig {
}

When I specify property from hand like this:

@TestPropertySource(properties = [
    "mybean.secret=xyz"
])
class MyBeanTest extends Specification {

It works. The property is read. But it's not my goal, cause there's much more properties in the project and it would become cumbersone to define them everywhere from hand.


Can you spot the problem I am missing in this code?

1 Answers

The missing puzzle was YamlPropertySourceFactory.

public class YamlPropertySourceFactory implements PropertySourceFactory {

    @Override
    public PropertySource<?> createPropertySource(String name, EncodedResource encodedResource) 
      throws IOException {
        YamlPropertiesFactoryBean factory = new YamlPropertiesFactoryBean();
        factory.setResources(encodedResource.getResource());

        Properties properties = factory.getObject();

        return new PropertiesPropertySource(encodedResource.getResource().getFilename(), properties);
    }
}

I was using yml properties files with nested properties:

mybean:
  secret: xyz

And Spring was not loading them correctly.

I had to update @PropertySource annotation as well as follow:

@Configuration
@PropertySource(
    value = ["classpath:context/default-properties.yml"],
  factory = YamlPropertySourceFactory.class
)
class PropertiesConfig {
}

Now it works like a charm .

I learned this on Baeldung website => https://www.baeldung.com/spring-yaml-propertysource

Related