如何将@WebMvcTest用于使用自动装配的ConversionService的Controller?

时间:2018-01-18 17:26:53

标签: java spring-mvc spring-boot spring-mvc-test

在Spring Boot应用程序中,我有两个POJO,FooBar,以及BarToFooConverter,它们看起来像:

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

我还有一个使用转换器的控制器:

@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);
    }
}

我想用@WebMvcTest来测试这个控制器,例如:

@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());
    }
}

但是当我执行此操作时,我发现我的BarToFooConverter未在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)

这似乎有道理,因为根据the Javadoc

  

使用此批注将禁用完全自动配置,而是仅应用与MVC测试相关的配置(即@ Controller,@ ControllerAdvice,@ JsonComponent Filter,WebMvcConfigurer和HandlerMethodArgumentResolver bean,但不包括@Component,@ Service或@Repository bean)。

但是,reference guide稍有不同,说@WebMvcTest 包含Converter s:

  

@WebMvcTest自动配置Spring MVC基础结构并将扫描的bean限制为@ Controller,@ ControllerAdvice,@ JsonComponent,Converter,GenericConverter,Filter,WebMvcConfigurer和HandlerMethodArgumentResolver。使用此批注时,不会扫描常规@Component bean。

这里的参考指南似乎不正确 - 或者我是否错误地注册了Converter

我也试过用我的测试嘲笑ConversionService

@WebMvcTest
@RunWith(SpringRunner.class)
public class TestControllerTest {
    @Autowired
    private MockMvc mockMvc;

    @MockBean
    private ConversionService conversionService;

    @Test
    public void test() throws Exception {
        when(conversionService.convert(any(Bar.class), eq(Foo.class))).thenReturn(new Foo("test"));

        mockMvc.perform(
                put("/test")
                        .contentType(MediaType.APPLICATION_JSON)
                        .content("{\"bar\":\"test\"}"))
                .andExpect(status().isOk());
    }
}

但现在Spring抱怨我的模拟ConversionService覆盖了默认模式:

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)
    ...

理想情况下,我想使用我原来的方法,在我的测试中使用真正的转换器而不是模拟ConversionService,但使用@WebMvcTest来限制启动的组件的范围,所以我还尝试在includeFilter注释中使用@WebMvcTest

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

但它仍然失败,原始的'无转换器发现能够转换...'错误消息。

这感觉就像是一个非常常见的要求 - 我错过了什么?

1 个答案:

答案 0 :(得分:3)

您可以使用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()); } } 带注释的方法手动注册转换器。您所要做的就是注入GenericConversionService并调用addConverter(new BarToFooConverter())以使转换器可解析。在这种情况下,你可以摆脱嘲弄的部分。您的测试可能如下所示:

MockMvc

替代解决方案:Spring版本 1.4.0 提供collection of test-related auto-configurations,其中一个自动配置为@AutoConfigureMockMvc,用于配置有效的{{1}}组件注入转换器组件很好。