2

I want to write a WebMvcTest test for a single controller in my Spring Boot application. Among other things there are some custom Converters in my application. Although they are not needed for this particular controller that I want to test, Spring tries to create them anyway.

Now the problem: those custom converters require more beans from my application which are not initialised by WebMvcTest test slice. And don't want to mock tens of beans which are completely irrelevant for the particular test. Apart from specifying them all manually in excludeFilters, what are best practises for excluding some web components from specific WebMvcTest tests?

Nikem
  • 5,716
  • 3
  • 32
  • 59
  • The best practice is to use `excludeFilters`. Why have you excluded that as a possible solution? A single controller-specific exclude filter is probably your most elegant option. – Andy Wilkinson Nov 29 '19 at 11:17
  • @AndyWilkinson First, adding similar `excludeFilters` to many tests is cumbersome. Second, whenever we add a new converter, we will have to update all tests to add one more exclude. We prefer to structure code by "domain", so there is no single package to exclude. – Nikem Nov 30 '19 at 09:55
  • 1
    You should be able to avoid that by creating your own annotation that is meta-annotated with `@WebMvcTest` and applies your custom filter. That custom filter would then be the one place that needs to be updated whenever a new converter is introduced. – Andy Wilkinson Nov 30 '19 at 12:44

2 Answers2

2

You could use a custom exclude filter in order to avoid loading converters into application context:

@RunWith(SpringRunner.class)
@WebMvcTest(controllers = YourController.class, excludeFilters = @ComponentScan.Filter(type = CUSTOM, classes = NoConvertersFilter.class))
public class YourControllerTest {
...
}

class NoConvertersFilter extends TypeExcludeFilter {

    private static final String CONVERTER_INTERFACE_NAME = Converter.class.getName();

    @Override
    public boolean match(@NonNull final MetadataReader metadataReader, @NonNull final MetadataReaderFactory metadataReaderFactory) throws IOException {

        return Arrays.asList(metadataReader.getClassMetadata().getInterfaceNames()).contains(CONVERTER_INTERFACE_NAME);
    }
}

With this approach you just have to add the excludeFilter to those controllers in which you don't want to have Converters loaded. No worries if a new converter is added: it'll be automatically excluded as far as it implements the converter interface.

Dharman
  • 30,962
  • 25
  • 85
  • 135
acontell
  • 6,792
  • 1
  • 19
  • 32
0

For custom tests don't use WebMvcTest, create a custom configuration:

@SpringBootTest
@WebAppConfiguration
@RunWith(value = SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {SomeYourTestConfiguration.class})
public class TestClass {

    private MockMvc mockMvc;

    @Before
    public void setup() {
        var someController = new SomeController();
        mockMvc = MockMvcBuilders.standaloneSetup(someController).addFilters(...) 
            .setMessageConverters(...).setControllerAdvice(...).setValidator(...);
    }

    @Test
    public void test() {
        //arrange
        when(...).thenReturn(...);
        //act
        var response = mockMvc.perform(...).andReturn().getResponse();
        //assert
        ...
    }
}

You can configure your mockMvc how you want.

Dmitry Ionash
  • 763
  • 5
  • 11
  • why shouldn't WebMvcTest be used? If I want to create a test slice that loads only the controllers that I need, how am I supposed to achieve this with SpringBootTest? You proposal would potentially force me to use @MockBean on dozens of components, correct? – João Matos Feb 13 '20 at 17:42