javaspring-bootjackson

Spring Boot tests and Jackson - Get an instance of ObjectMapper from context settings (application.yml) in tests without overhead config


I want to know what is the best way to get an instance of ObjectMapper from context settings in application.yml without "overhead" configuration(which means, WebAppConfiguration and ContextConfiguration) in Spring Boot, with only the minimized number of classes to load in the tests.

I already know that, to load Jackson-related config in application.yml(spring.jackson.xxx), you must @Autowired a Jackson2ObjectMapperBuilder, and use builder.build() to get an ObjectMapper.

So I create a configuration class:

@Configuration
public class JacksonConfig {
    @Autowired
    private Jackson2ObjectMapperBuilder objectMapperBuilder;

    @Bean
    public ObjectMapper objectMapper() {
        return objectMapperBuilder.build();
    }
}

And a wrapper class:

@Service
@Slf4j
public class JsonConverter {

    @Getter
    @Autowired
    private ObjectMapper mapper;

    public String objectToJson(Object obj) {
        try {
            return mapper.writeValueAsString(obj);
        } catch (JsonProcessingException e) {
            log.error("Cannot serialize object {} to JSON", obj);
            throw new ServiceException(ErrorCode.JSON_CONVERTER_EXCEPTION)
                    .addLog("log", e.getMessage());
        }
    }

    public Object jsonToObject(Class c, String json) {
        try {
            return mapper.readValue(json, c);
        } catch (IOException e) {
            log.error("Cannot deserialize JSON as instance of class {}", c.getName());
            throw ServiceException.wrap(e, ErrorCode.JSON_CONVERTER_EXCEPTION)
                    .addLog("json", json)
                    .addLog("target class", c.getName());
        }
    }

    public String messageErrorToJson(String message) {
        Map result = Collections.singletonMap("responseError", message);
        return objectToJson(result);
    }
}

That works in gradle bootRun, as well as in tests like this:

@Slf4j
@SpringBootTest // we can only launch the whole context to make Jackson2ObjectMapperBuilder to work
@WebAppConfiguration
@ContextConfiguration
class JsonConverterTest {

    @Autowired
    private JsonConverter jsonConverter;

    ...

I know it works, because when debugging it stops a breakpoint in JacksonAutoConfiguration class.

But, I wonder if there is another simpler way to get it. Why I have to use @WebAppConfiguration and @ContextConfiguration? Why doesn't @SpringBootTest load the application.yml spring.jackson.xx values?

Or, to make sure the application.yml is loaded, how to limit the classes to load in a SpringBootTest to minimize the number of classes to load, and speed up the tests?

EDIT: If I delete @WebAppConfiguration and ContextConfiguration, and add @SpringBootTest(class = MyApplication.class), it also loads all the context/launch the whole app and works.


Solution

  • Let us start from your last point

    If I delete @WebAppConfiguration and ContextConfiguration, and add @SpringBootTest(class = MyApplication.class), it also loads all the context/launch the whole app and works.

    Please check this link, with springboot 1.4 and onwards @SpringbootTest can be used in place of @ContextConfiguration (and some other test annotations)

    @WebAppConfiguration should be used only if you want the web part of your application to be initialized. For example if you want to test your controllers using MockMvc and so on.

    Not sure why you are asking if springboot test loads application.yml, Are you trying to load any properties from there during Test(I do not see here).