Preventing a @EnableWebMvc-annotated class from being picked up by @ComponentScan

StackOverflow https://stackoverflow.com/questions/21527578

  •  06-10-2022
  •  | 
  •  

Pregunta

I have the following test class:

@ActiveProfiles({ "DataTC", "test" })
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {BaseTestConfiguration.class, DataTestConfiguration.class, JpaConfiguration.class, PropertyPlaceholderConfiguration.class })
public class RegularDayToTimeSlotsTest {

    private static int NUMBER_OF_REGULAR_DAY_TO_TIME_SLOTS_IN_WEEK = 25;

    @Autowired
    private AdvertisementService advertisementService;

    @Test
    public void shouldNotContainSaturdayNorSunday() {
        Set<DayToTimeSlot> regularDayToTimeSlots = advertisementService.retrieveRegularDayToTimeSlots();
        assertThat(regularDayToTimeSlots).onProperty("day").excludes(Day.SATURDAY, Day.SUNDAY);
    }

    @Test
    public void shouldNotContainEveningNorNighttime() {
        Set<DayToTimeSlot> regularDayToTimeSlots = advertisementService.retrieveRegularDayToTimeSlots();
        assertThat(regularDayToTimeSlots).onProperty("timeSlot").excludes(TimeSlot.EVENING, TimeSlot.NIGHTTIME);
    }

    @Test
    public void shouldContainCorrectNumberOfDayToTimeSlots() {
        Set<DayToTimeSlot> regularDayToTimeSlots = advertisementService.retrieveRegularDayToTimeSlots();
        assertThat(regularDayToTimeSlots).hasSize(NUMBER_OF_REGULAR_DAY_TO_TIME_SLOTS_IN_WEEK);
    }
}

This is an integration test that does not need any ServletContext. Here is the BaseTestConfiguration class:

@Configuration
@ComponentScan(basePackages = { "com.bignibou" }, excludeFilters = { @Filter(type = FilterType.CUSTOM, value = RooRegexFilter.class),
        @Filter(type = FilterType.ANNOTATION, value = Controller.class), @Filter(type = FilterType.ANNOTATION, value = ControllerAdvice.class),
        @Filter(type = FilterType.ANNOTATION, value = EnableWebMvc.class) })
public class BaseTestConfiguration {

}

This is just a java configuration class that is meant to ensure that all of my application beans are instantiated and wired up correctly but that the class annotated with @EnableWebMvc is not taken into account because I don't need a Servlet context for this integration test.

Unfortunately, however much I try to exclude the Web/Mvc configuration class, it does not work and the @Configuration class annotated with @EnableWebMvc is also picked up.

I get this exception:

Caused by: java.lang.IllegalArgumentException: A ServletContext is required to configure default servlet handling
    at org.springframework.util.Assert.notNull(Assert.java:112)
    at org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer.<init>(DefaultServletHandlerConfigurer.java:54)
    at org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport.defaultServletHandlerMapping(WebMvcConfigurationSupport.java:329)
    at org.springframework.web.servlet.config.annotation.DelegatingWebMvcConfiguration$$EnhancerByCGLIB$$d1263fa1.CGLIB$defaultServletHandlerMapping$22(<generated>)
    at org.springframework.web.servlet.config.annotation.DelegatingWebMvcConfiguration$$EnhancerByCGLIB$$d1263fa1$$FastClassByCGLIB$$e0423f09.invoke(<generated>)
    at org.springframework.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:228)
    at org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor.intercept(ConfigurationClassEnhancer.java:326)
    at org.springframework.web.servlet.config.annotation.DelegatingWebMvcConfiguration$$EnhancerByCGLIB$$d1263fa1.defaultServletHandlerMapping(<generated>)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:606)
    at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:166)
    ... 43 more

Can anyone please help?

¿Fue útil?

Solución

There is in fact a cleaner way to configure such integration tests.

The answer lies in proper separation of concerns, not only at the class level but also at the package level.

It is always recommended that both implementation and configuration classes reside in package hierarchies that allow for proper separation of concerns. Specifically for a web application, it is highly advisable to ensure that web components and web configuration reside in a dedicated 'web' package. For example, you could consider a package hierarchy similar to the following:

  • com.example.domain
  • com.example.service
  • com.example.repository
  • com.example.web

With such a hierarchy you can simplify your component scanning configuration by including only those base packages that are pertinent to the current application or test scenario. By doing this there is typically no need to use exclusion filters for things you don't want. Instead, just specify packages you do want.

By the way, specifying "com.bignibou" as your base package is actually a worst practice since it is all-encompassing.

So give it a shot with explicit package inclusions (instead of excluding @Controller, @ControllerAdvice, etc.) and see if that doesn't make your life easier. ;)

Regards,

Sam

Otros consejos

I was able to get it working but at the price of a very polluted test class. Here it is:

@ActiveProfiles({ "DataTC", "test", "dev", "default" })
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = { BaseTestConfiguration.class, DataTestConfiguration.class, JpaConfiguration.class, RestConfiguration.class, DevMailConfiguration.class,
        PropertyPlaceholderConfiguration.class, ThymeleafConfiguration.class })
public class RegularDayToTimeSlotsTest {
...

Here is the BaseTestConfiguration class:

@Configuration
@ComponentScan(basePackages = { "com.bignibou" }, excludeFilters = { @Filter(type = FilterType.CUSTOM, value = RooRegexFilter.class),
        @Filter(type = FilterType.ANNOTATION, value = Controller.class), @Filter(type = FilterType.ANNOTATION, value = ControllerAdvice.class),
        @Filter(type = FilterType.ANNOTATION, value = Configuration.class) })
@ImportResource(value="classpath*:META-INF/spring/applicationContext-security.xml")
public class BaseTestConfiguration {
...

There must be a cleaner way of implementing and running integration tests... Any advice welcome...

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top