How can I use @WebMvcTest for a controller that uses auto-confident ConversionService?

There are two POJOs in the Spring Boot application, Foo and Bar and BarToFooConverter , which looks like this:

 @Component public class BarToFooConverter implements Converter<Bar, Foo> { @Override public Foo convert(Bar bar) { return new Foo(bar.getBar()); } } 

I also have a controller that uses a converter:

 @RestController("test") public class TestController { @Autowired private ConversionService conversionService; @RequestMapping(method = RequestMethod.PUT) @ResponseBody public Foo put(@RequestBody Bar bar) { return conversionService.convert(bar, Foo.class); } } 

I would like to test this controller using @WebMvcTest , something like:

 @WebMvcTest @RunWith(SpringRunner.class) public class TestControllerTest { @Autowired private MockMvc mockMvc; @Test public void test() throws Exception { mockMvc.perform( put("/test") .contentType(MediaType.APPLICATION_JSON) .content("{\"bar\":\"test\"}")) .andExpect(status().isOk()); } } 

but when I ran this, I found that my BarToFooConverter not registered in ConversionService :

 Caused by: org.springframework.core.convert.ConverterNotFoundException: No converter found capable of converting from type [com.example.demo.web.Bar] to type [com.example.demo.web.Foo] at org.springframework.core.convert.support.GenericConversionService.handleConverterNotFound(GenericConversionService.java:324) at org.springframework.core.convert.support.GenericConversionService.convert(GenericConversionService.java:206) at org.springframework.core.convert.support.GenericConversionService.convert(GenericConversionService.java:187) at com.example.demo.web.TestController.put(TestController.java:15) 

This seems to make sense because, according to Javadoc :

Using this annotation will disable full automatic configuration and instead only apply configurations related to MVC testing (e.g. @Controller, @ControllerAdvice, @JsonComponent Filter, WebMvcConfigurer and HandlerMethodArgumentResolver beans, but not @Component, @Service or @Repository beans).

However reference

but now Spring complains that my ConversionService layout overrides the default value:

 Caused by: java.lang.IllegalStateException: @Bean method WebMvcConfigurationSupport.mvcConversionService called as a bean reference for type [org.springframework.format.support.FormattingConversionService] but overridden by non-compatible bean instance of type [org.springframework.core.convert.ConversionService$$EnhancerByMockitoWithCGLIB$$da4e303a]. Overriding bean of same name declared in: null at org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor.obtainBeanInstanceFromFactory(ConfigurationClassEnhancer.java:402) at org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor.intercept(ConfigurationClassEnhancer.java:361) ... 

Ideally, I would like to use my original approach with a real converter in my test, and not scoff at ConversionService , but with @WebMvcTest to limit the scope of running components, so I also tried using a includeFilter in the @WebMvcTest annotation:

 @WebMvcTest(includeFilters = @ComponentScan.Filter(type = FilterType.REGEX, pattern = "com.example.demo.web.Bar*")) 

but it still does not work with the error message "There is no converter that can convert ...".

This is similar to what should be a fairly common requirement - what am I missing?

+5
source share
1 answer

You can register the converter manually in the @Before annotated method. All you have to do is enter the GenericConversionService and call addConverter(new BarToFooConverter()) to make the converter solvable. In this case, you can get rid of the mocking part. Your test might look like this:

 import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; import org.springframework.core.convert.support.GenericConversionService; import org.springframework.http.MediaType; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.web.servlet.MockMvc; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @WebMvcTest @RunWith(SpringRunner.class) public class TestControllerTest { @Autowired private MockMvc mockMvc; @Autowired private GenericConversionService conversionService; @Before public void setup() { conversionService.addConverter(new BarToFooConverter()); } @Test public void test() throws Exception { mockMvc.perform( put("/test") .contentType(MediaType.APPLICATION_JSON) .content("{\"bar\":\"test\"}")) .andExpect(status().isOk()); } } 

Alternative solution: Spring Download from version 1.4.0 provides a collection of test configuration related autoconfigurations and one of these autoconfigurations is @AutoConfigureMockMvc , which configures the MockMvc component, which works perfectly with the injection components of the converter.

+1
source

Source: https://habr.com/ru/post/1274793/


All Articles