Unit Testing of Spring MVC Controllers: Configuration

时间:2022-07-13 23:08:20

Original Link: http://www.petrikainulainen.net/programming/spring-framework/unit-testing-of-spring-mvc-controllers-configuration/

Unit Testing of Spring MVC Controllers: Configuration

by PETRI KAINULAINEN on JUNE 28, 2013

Writing unit tests for Spring MVC controllers has traditionally been both simple and problematic.

Although it is pretty simple to write unit tests which invoke controller methods, the problem is that those unit tests are not comprehensive enough.

For example, we cannot test controller mappings, validation and exception handling just by invoking the tested controller method.

Spring MVC Test solved this problem by giving us the possibility to invoke controller methods through the DispatcherServlet.

This is the first part of my tutorial which describes the unit testing of Spring MVC controllers and it describes how we can configure our unit tests.

Let’s get started.

为Spring MVC controllers写单元测试一直都是既简单又有问题.

虽然写调用controller方法的单元测试非常简单, 但问题在于那些测试并不够全面

例如, 仅仅调用测试的controller方法,我们不能测试controller的映射,验证和异常处理

Spring MVC Test通过给予我们通过DispatcherServlet调用controller方法的能力解决了这个问题

这是我Spring MVC controllers单元测试文章系列的第一部分, 内容是我们如何配置我们的单元测试

让我开始吧.

Getting the Required Dependencies with Maven

使用Maven获取需要的依赖包

We can get the required dependencies by declaring the following testing dependencies in ourpom.xml file:

我们可以在pom.xml文件中声明来获取以下需要的包

  • JUnit 4.11
  • Mockito Core 1.9.5
  • Spring Test 3.2.3.RELEASE

The relevant part of our pom.xml file looks as follows:

我们的pom.xml的相关部分如下:

<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.11</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.mockito</groupId>
    <artifactId>mockito-core</artifactId>
    <version>1.9.5</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-test</artifactId>
    <version>3.2.3.RELEASE</version>
    <scope>test</scope>
</dependency>

Let’s move on and take a quick look at our example application.

让我们继续, 快速看看我们的例子程序

The Anatomy of Our Example Application

实例程序剖析

The example application of this tutorial provides CRUD operations for todo entries. In order to understand the configuration of our test class, we must have some knowledge about the tested controller class.

这个教程的示例程序为todo条目提供了CRUD操作.为了理解我们的测试类的配置, 我们必须有一些关于测试controller的类的只是

At this point, we need to know the answers to these questions:

此时此刻,我们需要知道下列问题的答案:

  • What dependencies does it have?
  • 他有那些依赖
  • How is it instantiated?
  • 他是如何实例化的

We can get the answers to those questions by taking a look at the source code of theTodoController class. The relevant part of the TodoController class looks as follows:

看一看TodoController类的源代码我们可以找到那些问题的答案.相关的TodoController类的代码如下:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.MessageSource;
import org.springframework.stereotype.Controller;

@Controller
public class TodoController {

private final TodoService service;

private final MessageSource messageSource;

@Autowired
    public TodoController(MessageSource messageSource, TodoService service) {
        this.messageSource = messageSource;
        this.service = service;
    }

//Other methods are omitted.
}

As we can see, our controller class has two dependencies: TodoService and MessageSource. Also, we can see that our controller class uses constructor injection.

如我们所见, 我们的controller类有两个依赖: TodoService和MessageSource. 我们也可以看到controller使用了构造方法注入.

At this point this is all there information we need. Next we will talk about our application context configuration.

这就是此时我们需要的所有信息. 下面我们会谈谈我们程序上下文的设置

Configuring the Application Context

配置程序上下文

Maintaining a separate application context configurations for our application and our tests is cumbersome. Also, It can lead into problems if we change something in the application context configuration of our application but forget to do the same change for our test context.

为我们的程序和测试维护一个分离的程序上下文配置是很麻烦的. 如果我们改了我们程序的上下文设置但是忘了修改我们测试的上下文会导致一些问题.

That is why the application context configuration of the example application has been divided in a such way that we can reuse parts of it in our tests.

这就是为什么实例程序的上文环境被这样切分的原因, 我们可以在测试中重用它

Our application context configuration has been divided as follows:

我们程序的上下文配置被分成如下

  • The first application configuration class is called ExampleApplicationContext and it is the “main” configuration class of our application.
  • 第一个程序配置类叫做ExampleApplicationContext, 他是我们程序的"main"配置
  • The second configuration class is responsible of configuring the web layer of our application. The name of this class is WebAppContext and it is the configuration class which we will use in our tests.
  • 第二个配置类负责配置程序的web层.他的名字叫WebAppContext,我们会在测试中用到它
  • The third configuration class is called PersistenceContext and it contains the persistence configuration of our application.
  • 第三个配置类教唆PersistenceContext, 他包括程序的持久配置

Note: The example application has also a working application context configuration which uses XML configuration files. The XML configuration files which correspond with the Java configuration classes are: exampleApplicationContext.xml, exampleApplicationContext-web.xml andexampleApplicationContext-persistence.xml.

注意: 实例程序也有一个工作程序上下文配置, 它使用XML配置文件配置. 和这几个Java配置类一致的配置文件是: exampleApplicationContext.xml, exampleApplicationContext-web.xml, exampleApplicationContext-persistence.xml

Let’s take a look at the application context configuration of our web layer and find out how we can configure our test context.

让我们看一看我们的web层程序上下文配置和配置测试上下文的方法

Configuring the Web Layer

配置web层

The application context configuration of the web layer has the following responsibilities:

程序web层上下文配置有如下职责

  1. It enables the annotation driven Spring MVC.
  2. It configures the location of static resources such as CSS files and Javascript files.
  3. It ensures that the static resources are served by the container’s default servlet.
  4. It ensures that the controller classes are found during component scan.
  5. It configures the ExceptionResolver bean.
  6. It configures the ViewResolver bean.
  1. 开启Spring MVC注解驱动
  2. 设置静态资源如CSS和JS文件的路径
  3. 保证静态资源由容器的默认servlet提供
  4. 保证controller类在组件扫描时被找到
  5. 配置 ExceptionResolver bean
  6. 配置 ViewResolver bean

Let’s move on and take a look at the Java configuration class and the XML configuration file.

让我们继续, 看看Java的配置类和XML配置文件

Java Configuration

Java 配置

If we use Java configuration, the source code of the WebAppContext class looks as follows:

如果我们使用Java配置, WebAppContext类的源代码如下

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.servlet.handler.SimpleMappingExceptionResolver;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
import org.springframework.web.servlet.view.JstlView; import java.util.Properties; @Configuration
@EnableWebMvc
@ComponentScan(basePackages = {
"net.petrikainulainen.spring.testmvc.common.controller",
"net.petrikainulainen.spring.testmvc.todo.controller"
})
public class WebAppContext extends WebMvcConfigurerAdapter { @Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/static/**").addResourceLocations("/static/");
} @Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
} @Bean
public SimpleMappingExceptionResolver exceptionResolver() {
SimpleMappingExceptionResolver exceptionResolver = new SimpleMappingExceptionResolver(); Properties exceptionMappings = new Properties(); exceptionMappings.put("net.petrikainulainen.spring.testmvc.todo.exception.TodoNotFoundException", "error/404");
exceptionMappings.put("java.lang.Exception", "error/error");
exceptionMappings.put("java.lang.RuntimeException", "error/error"); exceptionResolver.setExceptionMappings(exceptionMappings); Properties statusCodes = new Properties(); statusCodes.put("error/404", "404");
statusCodes.put("error/error", "500"); exceptionResolver.setStatusCodes(statusCodes); return exceptionResolver;
} @Bean
public ViewResolver viewResolver() {
InternalResourceViewResolver viewResolver = new InternalResourceViewResolver(); viewResolver.setViewClass(JstlView.class);
viewResolver.setPrefix("/WEB-INF/jsp/");
viewResolver.setSuffix(".jsp"); return viewResolver;
}
}

XML Configuration

XML 配置文件

If we use XML configuration, the content of the exampleApplicationContext-web.xml file looks as follows:

如果我们使用XML配置, exampleApplicationContext-web.xml文件如下:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd"> <mvc:annotation-driven/> <mvc:resources mapping="/static/**" location="/static/"/>
<mvc:default-servlet-handler/> <context:component-scan base-package="net.petrikainulainen.spring.testmvc.common.controller"/>
<context:component-scan base-package="net.petrikainulainen.spring.testmvc.todo.controller"/> <bean id="exceptionResolver" class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<property name="exceptionMappings">
<props>
<prop key="net.petrikainulainen.spring.testmvc.todo.exception.TodoNotFoundException">error/404</prop>
<prop key="java.lang.Exception">error/error</prop>
<prop key="java.lang.RuntimeException">error/error</prop>
</props>
</property>
<property name="statusCodes">
<props>
<prop key="error/404">404</prop>
<prop key="error/error">500</prop>
</props>
</property>
</bean> <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
</bean>
</beans>

Configuring the Test Context

配置测试上下文

The configuration of our test context has two responsibilities:

测试上下文配置的职责有2个:

  1. It configures a MessageSource bean which is used by our controller class (feedback messages) and Spring MVC (validation error messages). The reason why we need to do this is that theMessageSource bean is configured in the “main” configuration class (or file) of our application context configuration.
  2. It creates a TodoService mock which is injected to our controller class.
  1. 配置一个给controller类使用(反馈信息)和Spring MVC(验证错误信息)使用的MessageSource bean. 我们需要这么做的原因是 MessageSource bean是在我们程序上下文配置的"main"配置类(或文件)配置的
  2. 他创建一个TodoService mock, 用来注入到controller类中

Let’s find out how we configure our test context by using Java configuration class and XML configuration file.

让我们看看我们如何用Java配置类和配置文件来配置我们的测试上下文

Java Configuration

If we configure our test context by using Java configuration, the source code of the TestContextclass looks as follows:

如果我们使用Java配置来配置测试环境, TestContext的源代码如下:

import org.mockito.Mockito;
import org.springframework.context.MessageSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.ResourceBundleMessageSource; @Configuration
public class TestContext { @Bean
public MessageSource messageSource() {
ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource(); messageSource.setBasename("i18n/messages");
messageSource.setUseCodeAsDefaultMessage(true); return messageSource;
} @Bean
public TodoService todoService() {
return Mockito.mock(TodoService.class);
}
}

XML Configuration

If we configure our test context by using an XML configuration, the content of thetestContext.xml file looks as follow:

如果使用XML文件来配置测试上下文, testContext.xml 文件如下:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basename" value="i18n/messages"/>
<property name="useCodeAsDefaultMessage" value="true"/>
</bean> <bean id="todoService" name="todoService" class="org.mockito.Mockito" factory-method="mock">
<constructor-arg value="net.petrikainulainen.spring.testmvc.todo.service.TodoService"/>
</bean>
</beans>

Configuring The Test Class

配置测试类

We can configure our test class by using one of the following options:

我们可以通过如下选择二选一来配置我们的测试类:

  1. The Standalone configuration allows us to register one or more controllers (classes annotated with the @Controller annotation) and configure the Spring MVC infrastructure programatically. This approach is a viable option if our Spring MVC configuration is simple and straight-forward.
  2. The WebApplicationContext based configuration allows us the configure Spring MVC infrastructure by using a fully initialized WebApplicationContext. This approach is better if our Spring MVC configuration is so complicated that using standalone configuration does not make any sense.
  1. 单独的配置允许我们注册一个或多个controller(通过在类上使用@Controller注释标记)并编程式配置Spring MVC的基础设施.如果Spring MVC配置足够简单和直接, 这个方法是可行的
  2. 基于配置的WebApplicationContext允许我们通过完全初始化一个WebApplicationContext来配置Spring MVC的基础设施. 如果我们的Spring MVC配置很复杂, 使用单独的配置不好使的话,这将会是很好的方法

Let’s move on and find out how we can configure our test class by using both configuration options.

让我们看看使用这两种方法如何配置test类

Using Standalone Configuration

使用单独的配置

We can configure our test class by following these steps:

我们可以按如下步骤配置我们的测试类:

  1. Annotate the class with the @RunWith annotation and ensure that test is executed by using the MockitoJUnitRunner.
  2. Add a MockMvc field to the test class.
  3. Add a TodoService field to the test class and annotate the field with the @Mock annotation. This annotation marks the field as a mock. The field is initialized by the MockitoJUnitRunner.
  4. Add a private exceptionResolver() method to the class. This method creates a newSimpleMappingExceptionResolver object, configures it, and returns the created object.
  5. Add a private messageSource() method to the class. This method creates a newResourceBundleMessageSource object, configures it, and returns the created object.
  6. Add a private validator() method to the class. This method creates a newLocalValidatorFactoryBean object and returns the created object.
  7. Add a private viewResolver() method to the the class. This method creates a newInternalResourceViewResolver object, configures it, and returns the created object.
  8. Add a setUp() method to the test class and annotate the method with the @Beforeannotation. This ensures that the method is invoked before each test. This method creates a new MockMvc object by calling the standaloneSetup() method of the MockMvcBuilders class and configures the Spring MVC infrastructure programmatically.
  1. 使用@RunWith注释类,保证测试是使用 MockitoJUnitRunner 执行的
  2. 加一个 MockMvc 字段到测试类中
  3. 加一个TodoService字段到test类中,并使用@Mock注释标记他. 这个注释让这个字段编程mock对象. 这个字段使用MockitoJUnitRunner初始化
  4. 增加一个private exceptionResolver()方法到类中.这个方法创建一个新的SimpleMappingExceptionResolver对象,配置他并返回创建的对象
  5. 增加一个private messageSource()方法到类中.这个方法创建一个新的ResourceBundleMessageSource对象,配置他并返回创建的对象
  6. 增加一个private validator()方法到类中.这个方法创建一个新的LocalValidatorFactoryBean对象并返回创建的对象
  7. 增加一个private viewResolver()方法到类中.这个方法创建一个新的InternalResourceViewResolver对象,配置他并返回创建的对象.
  8. 增加一个setUp()方法到测试类中并用@Before注释标记.这保证了方法在每次测试之前执行.这个方法通过调用MockMvcBuilders类的standaloneSetup()创建一个新的MockMvc对象,并通过编码配置了Spring MVC的基础设置

The source code of our test class looks as follows:

源代码如下

import org.junit.Before;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import org.springframework.context.MessageSource;
import org.springframework.context.support.ResourceBundleMessageSource;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.handler.SimpleMappingExceptionResolver;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
import org.springframework.web.servlet.view.JstlView; import java.util.Properties; @RunWith(MockitoJUnitRunner.class)
public class StandaloneTodoControllerTest { private MockMvc mockMvc; @Mock
private TodoService todoServiceMock; @Before
public void setUp() {
mockMvc = MockMvcBuilders.standaloneSetup(new TodoController(messageSource(), todoServiceMock))
.setHandlerExceptionResolvers(exceptionResolver())
.setValidator(validator())
.setViewResolvers(viewResolver())
.build();
} private HandlerExceptionResolver exceptionResolver() {
SimpleMappingExceptionResolver exceptionResolver = new SimpleMappingExceptionResolver(); Properties exceptionMappings = new Properties(); exceptionMappings.put("net.petrikainulainen.spring.testmvc.todo.exception.TodoNotFoundException", "error/404");
exceptionMappings.put("java.lang.Exception", "error/error");
exceptionMappings.put("java.lang.RuntimeException", "error/error"); exceptionResolver.setExceptionMappings(exceptionMappings); Properties statusCodes = new Properties(); statusCodes.put("error/404", "404");
statusCodes.put("error/error", "500"); exceptionResolver.setStatusCodes(statusCodes); return exceptionResolver;
} private MessageSource messageSource() {
ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource(); messageSource.setBasename("i18n/messages");
messageSource.setUseCodeAsDefaultMessage(true); return messageSource;
} private LocalValidatorFactoryBean validator() {
return new LocalValidatorFactoryBean();
} private ViewResolver viewResolver() {
InternalResourceViewResolver viewResolver = new InternalResourceViewResolver(); viewResolver.setViewClass(JstlView.class);
viewResolver.setPrefix("/WEB-INF/jsp/");
viewResolver.setSuffix(".jsp"); return viewResolver;
}
}

Using the standalone configuration has two problems:

使用单独的配置有两个问题

  1. Our test class looks like a mess even though our Spring MVC configuration is rather simple. Naturally, we could clean it up by moving the creation of Spring MVC infrastructure components into a separate class. This is left as an exercise for the reader.
  2. We have to duplicate the configuration of Spring MVC infrastructure components. This means that if we change something in the application context configuration of our application, we must remember to do the same change to our tests as well.
  1. 就算Spring MVC的配置很简单,测试类看起来也是一团糟. 当然,我们可以将这些创建Spring MVC基础设施的代码移到一个单独的class中. 这个就留作为读者的练习好了.
  2. 我们不得不重复写一次Spring MVC基础设置组件的配置.这意味着我们改变了程序的上下文的时候, 必须对我们的测试上下文做出相同的修改.

Using WebApplicationContext Based Configuration

使用 基于WebApplicationContext的配置

We can configure our test class by following these steps:

我们可以按如下步骤配置我们的测试类:

  1. Annotate the test class with the @RunWith annotation and ensure that the test is executed by using the SpringJUnit4ClassRunner.
  2. Annotate the class with the @ContextConfiguration annotation and ensure that the correct configuration classes (or XML configuration files) are used. If we want to use Java configuration, we have to set the configuration classes as the value of the classes attribute. On the other hand, if we prefer XML configuration, we have to set the configuration files as the value of the locations attribute.
  3. Annotate the class with the @WebAppConfiguration annotation. This annotation ensures that the application context which is loaded for our test is a WebApplicationContext.
  4. Add a MockMvc field to the test class.
  5. Add a TodoService field to the test class and annotate the field with the @Autowiredannotation.
  6. Add a WebApplicationContext field to the test class and annotate the field with the@Autowired annotation.
  7. Add a setUp() method to the test class and annotate the method with the @Before annotation. This ensures that the method is called before each test. This method has responsibilities: it resets the service mock before each test and create a new MockMvc object by calling the webAppContextSetup() method of the MockMvcBuilders class.
  1. 使用@RunWith注释标记测试类, 并保证测试是使用SpringJUnit4ClassRunner执行的
  2. 使用@ContextConfiguration注释并保证使用正确的配置类(或者XML配置文件). 如果我们先使用Java配置,就必须设置配置类为 classes 参数的值. 而如果线使用XML配置, 则必须设置配置文件为locations参数的值
  3. 使用@WebAppConfiguration注释标记类.这个注释保证了我们的测试加载的程序上下文是一个WebApplicationContext, 他的作用是表明我们的测试程序会使用默认的web根目录 -- "src/main/webapp", 另外, 他必须和ContextConfiguration结合使用.如果要修改web根目录的值, 使用value参数覆盖它
  4. 加入一个 MockMvc字段到测试类中.
  5. 加入一个TodoService字段到测试类中,并使用@Autowired注释标注他
  6. 加入一个WebApplicationContext字段到测试类中,并使用@Autowired注释标注他
  7. 加入一个setUp()方法到测试类中,并使用@Before注释标注他.这保证了这个方法会在每次测试之前被调用.这个方法有如下职责: 他在每次测试前重置service mock并通过调用MockMvcBuilders类的webAppContextSetup创建新的MockMvc对象.

The source code of our test class looks as follows:

源代码如下

import org.junit.Before;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext; @RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {TestContext.class, WebAppContext.class})
//@ContextConfiguration(locations = {"classpath:testContext.xml", "classpath:exampleApplicationContext-web.xml"})
@WebAppConfiguration
public class WebApplicationContextTodoControllerTest { private MockMvc mockMvc; @Autowired
private TodoService todoServiceMock; @Autowired
private WebApplicationContext webApplicationContext; @Before
public void setUp() {
//We have to reset our mock between tests because the mock objects
//are managed by the Spring container. If we would not reset them,
//stubbing and verified behavior would "leak" from one test to another.
Mockito.reset(todoServiceMock); mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
}
}

The configuration of our test class looks a lot cleaner than the configuration which uses standalone configuration. However, the “downside” is that our test uses the full Spring MVC infrastructure. This might be an overkill if our test class really uses only a few components.

这种测试类的配置方法看起来比使用单独的配置类的方法清晰多了.然而, 坏处就是我们的测试使用了完全的Spring MVC基础设置.如果我们仅仅使用一小部分组件这可能会太过于消耗性能

Summary
总结

We have now configured our unit test class by using both the standalone setup and theWebApplicationContext based setup. This blog post has taught us two things:

我们现在已经通过单独配置和基于WebApplicationContext的方法配置好了单元测试类. 这篇blog教会了我们两件事情

  • We learned that it is important to divide the application context configuration in a such way that we can reuse parts of it in our tests.
  • 我们学到了分离程序上下文以在测试中重用他们的重要性
  • We learned the difference between the standalone configuration and theWebApplicationContext based configuration.
  • 我们学到了单独配置和基于WebApplicationContext的配置的重要性

The next part of this tutorial describes how we can write unit tests for “normal” Spring MVC controllers.

下一部分教程将会描述如何为普通的SpringMVC contoller写单元测试

P.S. The example application of this blog post is available at Github.

另外, 实例程序已经发布到了Github上