SpringBoot unit test not getting @Value correctely of a @Component autowired object

Viewed 115

I'm implementing a unit test that is very sample, it's increment a counter of google guava library CacheLoader and get the value.

RateLimitTest

@RunWith(SpringRunner.class)
@ContextConfiguration(classes = RateLimitTestConfiguration.class)
public class RateLimitTest {

    @Autowired
    RateLimitFilter rateLimitFilter;

    @Test
    public void incrementCounter_whenGetCounter_existsKey_and_returnIncrementedValue() throws ExecutionException {
        final int times = 10;
        final String counterName = "TestCounter";
        int i = 0;
        for(i = 0; i < 10; i++)  {
            rateLimitFilter.getRequestCountPerApiKey().put(counterName,i);
        }
        Assert.assertNotNull(rateLimitFilter.getRequestCountPerApiKey().get(counterName));
        Assert.assertEquals(Long.valueOf(times),Long.valueOf(rateLimitFilter.getRequestCountPerApiKey().get(counterName)));
    }

}

So I has implemented a @Bean on tests context:

@Configuration
public class RateLimitTestConfiguration {

    @Bean
    public RateLimitFilter rateLimitFilter() {
        return new RateLimitFilter();
    }

}

This is the class of application normal:

@Component
public class RateLimitFilter implements GatewayFilter {

    final Logger LOGGER = LoggerFactory.getLogger(RateLimitFilter.class);

    @Value("${throttling.request.rate.minute}")
    private int MAX_REQUEST_PER_MINUTE;

    private LoadingCache<String,Integer> requestCountPerApiKey;

    ....

public LoadingCache<String, Integer> getRequestCountPerApiKey() {
        return requestCountPerApiKey;
    }
}

on the application.yml of tests I've set:

throttling:
  request:
    rate:
      minute: 5

And Test fails with error:

 Caused by: org.springframework.beans.TypeMismatchException: Failed to convert value of type 'java.lang.String' to required type 'int'; nested exception is java.lang.NumberFormatException: For input string: "${throttling.request.rate.minute}"
        at org.springframework.beans.TypeConverterSupport.convertIfNecessary(TypeConverterSupport.java:79)
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1252)
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1224)
        at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:640)
        ... 55 common frames omitted
    Caused by: java.lang.NumberFormatException: For input string: "${throttling.request.rate.minute}"
        at java.base/java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
        at java.base/java.lang.Integer.parseInt(Integer.java:638)
        at java.base/java.lang.Integer.valueOf(Integer.java:983)
        at org.springframework.util.NumberUtils.parseNumber(NumberUtils.java:211)
        at org.springframework.beans.propertyeditors.CustomNumberEditor.setAsText(CustomNumberEditor.java:115)
        at org.springframework.beans.TypeConverterDelegate.doConvertTextValue(TypeConverterDelegate.java:429)
        at org.springframework.beans.TypeConverterDelegate.doConvertValue(TypeConverterDelegate.java:402)
        at org.springframework.beans.TypeConverterDelegate.convertIfNecessary(TypeConverterDelegate.java:155)
        at org.springframework.beans.TypeConverterSupport.convertIfNecessary(TypeConverterSupport.java:73)
1 Answers

There are a lot of approaches to make it work. I'll give 2 of them:

  1. You can simply replace @ContextConfiguration(classes = RateLimitTestConfiguration.class) with @SpringBootTest(classes = RateLimitTestConfiguration.class). Works only with Spring Boot.
  2. You can add PropertySourcesPlaceholderConfigurer bean manually to your configuration (the 1st approach configures this bean automatically among others):
@Bean
public PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
    PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer = new PropertySourcesPlaceholderConfigurer();
    YamlPropertiesFactoryBean yaml = new YamlPropertiesFactoryBean();
    yaml.setResources(new ClassPathResource("application.yml"));
    propertySourcesPlaceholderConfigurer.setProperties(yaml.getObject());
    return propertySourcesPlaceholderConfigurer;
}
Related