javaspringspring-boot

How can I fix TypeConverterSupport for Spring Boot 3.x when using ContextConfiguration classes?


I have some basic yaml like

myThing:
  someValue: 10

and I'm running my tests and running into formatting exceptions surrounding

TypeConverterSupport.convertIfNecessary(TypeConverterSupport.java:80)

where it's trying to parse strings to Integers, or Integers to Strings, or Strings to LocalDate, all things that my application is able to do fine when it boots up the entire Spring Boot context with an annotation like @SpringBootTest

edit: I'm thinking I'm just missing a necessary class for this behavior in @ContextConfiguration but I can't find it for the life of me and I've spent hours on this.

package com.example.demo;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Component
public class MyThing {
    @Value("${myThing.myValue}")
    private Integer myValue;

    public Integer getMyValue() {
        return myValue;
    }
}

package com.example.demo;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension;

import static org.junit.jupiter.api.Assertions.assertEquals;

@ExtendWith(SpringExtension.class)
@ContextConfiguration(classes = {MyThing.class}) // Won't be able to load the context
class DemoApplicationTests {

    @Autowired
    private MyThing myThing;

    @Test
    void contextLoads() {
        assertEquals(10, myThing.getMyValue());
    }

}
myThing:
  myValue: 10

Caused by: java.lang.NumberFormatException: For input string: "${myThing.myValue}" at java.base/java.lang.NumberFormatException.forInputString(NumberFormatException.java:67) at java.base/java.lang.Integer.parseInt(Integer.java:654) at java.base/java.lang.Integer.valueOf(Integer.java:999) at org.springframework.util.NumberUtils.parseNumber(NumberUtils.java:203) at org.springframework.beans.propertyeditors.CustomNumberEditor.setAsText(CustomNumberEditor.java:115) at org.springframework.beans.TypeConverterDelegate.doConvertTextValue(TypeConverterDelegate.java:439) at org.springframework.beans.TypeConverterDelegate.doConvertValue(TypeConverterDelegate.java:412) at org.springframework.beans.TypeConverterDelegate.convertIfNecessary(TypeConverterDelegate.java:161) at org.springframework.beans.TypeConverterSupport.convertIfNecessary(TypeConverterSupport.java:80) ... 39 more

Using @SpringBootTest loads the full context with the converter service and the test passes.


Solution

  • To be able to load properties from application.properties file in test which is marked simple with @ExtendWith(SpringExtension.class) and not with @SpringBootTest, you must add ConfigDataApplicationContextInitializer.class to the ContextConfiguration annotation:

    import org.junit.jupiter.api.Test;
    import org.junit.jupiter.api.extension.ExtendWith;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.test.context.ConfigDataApplicationContextInitializer;
    import org.springframework.test.context.ContextConfiguration;
    import org.springframework.test.context.junit.jupiter.SpringExtension;
    
    import static org.junit.jupiter.api.Assertions.assertEquals;
    
    @ExtendWith(SpringExtension.class)
    @ContextConfiguration(
            initializers = ConfigDataApplicationContextInitializer.class,
            classes = MyThing.class
    )
    class Testik {
    
        @Autowired
        private MyThing myThing;
    
        @Test
        void contextLoads() {
            assertEquals(10, myThing.getMyValue());
        }
    }