22
votes

From Spring 3.1, we can use JavaConfig more easily thanks to @Enable* annotations.

So I made a WebConfig to set WebMvc configuration, and tried to test it. But if I extends WebMvcConfigurerAdapter or WebMvcConfigurationSupport with WebConfig the unit test fails because of lack of ServletContext. The code and messages look like below.

WebConfig.java

@Configuration
@EnableWebMvc
public class WebConfig extends WebMvcConfigurationSupport {}

Test.java

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes=WebConfig.class)
public class TestFail {
    @Test
    public void test() {}
}

Message

java.lang.IllegalStateException: Failed to load ApplicationContext
    at org.springframework.test.context.TestContext.getApplicationContext(TestContext.java:157)
    at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.injectDependencies(DependencyInjectionTestExecutionListener.java:109)
    at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.prepareTestInstance(DependencyInjectionTestExecutionListener.java:75)
...
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:253)
    at com.zum.news.comments.web.WebConfig$$EnhancerByCGLIB$$8bbfcca1.CGLIB$defaultServletHandlerMapping$10(<generated>)
    at com.zum.news.comments.web.WebConfig$$EnhancerByCGLIB$$8bbfcca1$$FastClassByCGLIB$$19b86ad0.invoke(<generated>)
    at net.sf.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:215)
    at org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor.intercept(ConfigurationClassEnhancer.java:280)
    at com.zum.news.comments.web.WebConfig$$EnhancerByCGLIB$$8bbfcca1.defaultServletHandlerMapping(<generated>)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:149)
    ... 41 more

How to unit test the WebConfig properly?

Edit

As Garcia said, this bug is fixed in Spring 3.2.0.RC1.

Just add @WebAppConfiguration annotation in the test class.

@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ContextConfiguration(classes=WebConfig.class)
public class TestFail {
    @Test
    public void test() {}
}
4
I don't understand how this question has so few votes. I suppose there are people out there trying to do integration tests with Spring 3.1 niceties... - Guido
The question was precisely what I was looking for and the answer is included in the "Edit" section - Markus Pscheidt

4 Answers

12
votes

As Guido mentioned previously, this has been solved as of 3.2. The following are the details of how to take advantage of the new test improvements. To ensure that a servlet context is loaded for your test, you need to annotate your test with @WebAppConfiguration and define AnnotationConfigWebContextLoader as your context loader, as below:

@RunWith(SpringJUnit4ClassRunner.class)    
@WebAppConfiguration
@ContextConfiguration(
    classes = MyWebConfig.class, 
    loader = AnnotationConfigWebContextLoader.class)
public class MyTest {
    //...
}
7
votes

If @EnableWebMvc annotation require ServletContext then I suggest to split your config to beans definitions which will be used in unit tests and other configuration which used by application and framework. In this case application will import both configs and unit tests will import only one.

BeansConfig.java:

@Configuration
public class BeansConfig {
    @Bean
    MyBean myBean() {
        return new MyBean()
    }
}

WebConfig.java:

@Configuration
@EnableWebMvc
@Import(BeansConfig.class)
public class WebConfig extends WebMvcConfigurationSupport {}

TestFail.java:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes=BeansConfig.class)
public class TestFail {
    @Test
    public void test() {}
}
6
votes

There is a bug in Spring 3.1, you can find the answer in these two issues:

Please let us know if you find a workaround for Spring 3.1, if not we must wait until 3.2 is out there. I have to say that I've just tried it with Spring 3.2.0.M2 and it is still not working for me.

2
votes

Another recommendation that I have would be to use spring-test-mvc, which internally creates a mock servlet context for the Controller tests to work.

If you want to continue with your approach, you may then have to create your own Spring Context loader that additionally initializes a Mock servlet context - along these lines: http://tedyoung.me/2011/02/14/spring-mvc-integration-testing-controllers/, From Spring-test-mvc source