web-dev-qa-db-ja.com

自動配線されたConversionServiceを使用するコントローラーに@WebMvcTestを使用するにはどうすればよいですか?

Spring Bootアプリケーションには、FooBarの2つのPOJOと、次のような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());
    }
}

しかし、これを実行すると、BarToFooConverterConversionServiceに登録されていないことがわかりました。

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)

Javadoc によると、これは理にかなっているようです。

このアノテーションを使用すると、完全な自動構成が無効になり、代わりにMVCテストに関連する構成のみが適用されます(つまり、@ Controller、@ ControllerAdvice、@ JsonComponent Filter、WebMvcConfigurer、およびHandlerMethodArgumentResolver Beanであり、@ Component、@ Service、または@Repository Beanは適用されません)。

ただし、 リファレンスガイド は少し異なり、@WebMvcTestにはConvertersが含まれます

@WebMvcTestは、Spring MVCインフラストラクチャを自動構成し、スキャンされたBeanを@ Controller、@ ControllerAdvice、@ JsonComponent、Converter、GenericConverter、Filter、WebMvcConfigurer、およびHandlerMethodArgumentResolverに制限します。このアノテーションを使用する場合、通常の@ComponentBeanはスキャンされません。

ここではリファレンスガイドが間違っているようです-または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をモックするのではなく、実際のConverterをテストに使用し、開始するコンポーネントの範囲を制限するために@WebMvcTestを使用して、元のアプローチを使用したいと思います。 @WebMvcTestアノテーションでincludeFilterを使用してみました。

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

しかし、それでも元の「変換できるコンバーターが見つかりません...」というエラーメッセージで失敗します。

これは非常に一般的な要件でなければならない何かのように感じます-私は何が欠けていますか?

7
DaveyDaveDave

コンバーターは、_@Before_アノテーション付きメソッドで手動で登録できます。 GenericConversionService を挿入し、 addConverter(new BarToFooConverter()) を呼び出して、コンバーターを解決可能にするだけです。この場合、モック部分を取り除くことができます。テストは次のようになります。

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

代替ソリューション:バージョン1.4.0以降のSpring Bootは テスト関連の自動構成のコレクション およびこれらの自動の1つを提供します-構成は _@AutoConfigureMockMvc_ これは、注入されたコンバーターコンポーネントで正常に機能するMockMvcコンポーネントを構成します。

3
Szymon Stepniak