Search code examples
springspring-bootkotlinjunit5spring-boot-test

Spring @Value injection conversion not working in @SpringJUnitConfig test


I couldn't find a way to make the automatic value conversion when injecting @Value work when using @SpringJUnitConfig test.

For example, for the given code:

@SpringBootApplication
class DemoApplication

fun main(args: Array<String>) {
    runApplication<DemoApplication>(*args)
}

@Component
class ValueConvertInjection(@Value("2ms") val duration : java.time.Duration) {
    @PostConstruct
    fun init() = println("Duration converted: $duration")
}

The given test will fail:

@SpringJUnitConfig(classes = [ValueConvertInjection::class])
class JUnitValueConvertInjectionTest {
    @Autowired
    lateinit var vi :ValueConvertInjection

    @Test
    fun test() = assert(vi.duration == Duration.ofMillis(2))
    
}

with the following exception:

...
Caused by: org.springframework.beans.ConversionNotSupportedException: Failed to convert value of type 'java.lang.String' to required type 'java.time.Duration'; nested exception is java.lang.IllegalStateException: Cannot convert value of type 'java.lang.String' to required type 'java.time.Duration': no matching editors or conversion strategy found
    at org.springframework.beans.TypeConverterSupport.convertIfNecessary(TypeConverterSupport.java:76)
    ... 85 more
Caused by: java.lang.IllegalStateException: Cannot convert value of type 'java.lang.String' to required type 'java.time.Duration': no matching editors or conversion strategy found
    at org.springframework.beans.TypeConverterDelegate.convertIfNecessary(TypeConverterDelegate.java:262)
    at org.springframework.beans.TypeConverterSupport.convertIfNecessary(TypeConverterSupport.java:73)
    ... 89 more

I couldn't find what I should add to the context so any of the Spring conversions will be registered and picked.


Solution

  • Spring Boot intializes the context with a conversion service, which is what you need:

    public class ConversionConfiguration {
        @Bean
        fun conversionService() = ApplicationConversionService.getSharedInstance()
    }
    
    @SpringJUnitConfig(classes = [
            ValueConvertInjection::class,
            ConversionConfguration::class
    ]) {
    class JUnitValueConvertInjectionTest {
        @Autowired
        lateinit var vi: ValueConvertInjection
    
        @Test
        fun test() = assert(vi.duration == Duration.ofMillis(2))   
    }