spring mvc 测试

简介:

随着RESTful Web Service的流行,测试对外的Service是否满足期望也变的必要的。从Spring 3.2开始Spring了Spring Web测试框架,如果版本低于3.2,请使用spring-test-mvc项目(合并到spring3.2中了)。

 Spring MVC测试框架提供了对服务器端和客户端(基于RestTemplate的客户端)提供了支持。

 对于服务器端:在Spring 3.2之前,我们测试时一般都是直接new控制器,注入依赖,然后判断返回值。但是我们无法连同Spring MVC的基础设施(如DispatcherServlet调度、类型转换、数据绑定、拦截器等)一起测试,另外也没有现成的方法测试如最终渲染的视图 (@ResponseBody生成的JSON/XML、JSP、Velocity等)内容是否正确。从Spring 3.2开始这些事情都可以完成了。而且可以测试完整的Spring MVC流程,即从URL请求到控制器处理,再到视图渲染都可以测试。

 对于客户端:不需要启动服务器即可测试我们的RESTful 服务。1 服务器端测试

我的环境:JDK7、Maven3、spring4、Servlet3

首先添加依赖

如下是spring-context和spring-webmvc依赖:

1
2
3
4
5
6
7
8
9
10
11
         <dependency>
             <groupId>org.springframework</groupId>
             <artifactId>spring-context</artifactId>
             <version>${spring.version}</version>
         </dependency>
 
         <dependency>
             <groupId>org.springframework</groupId>
             <artifactId>spring-webmvc</artifactId>
             <version>${spring.version}</version>
         </dependency>

版本信息:<spring.version>4.0.0.RELEASE</spring.version>

 

如下是测试相关的依赖(junit、hamcrest、mockito、spring-test):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
         <dependency>
             <groupId>junit</groupId>
             <artifactId>junit</artifactId>
             <version>${junit.version}</version>
             <scope>test</scope>
         </dependency>
 
         <dependency>
             <groupId>org.hamcrest</groupId>
             <artifactId>hamcrest-core</artifactId>
             <version>${hamcrest.core.version}/version>
             <scope>test</scope>
         </dependency>
         <dependency>
             <groupId>org.mockito</groupId>
             <artifactId>mockito-core</artifactId>
             <version>${mockito.core.version}</version>
             <scope>test</scope>
         </dependency>
 
         <dependency>
             <groupId>org.springframework</groupId>
             <artifactId>spring-test</artifactId>
             <version>${spring.version}</version>
             <scope>test</scope>
         </dependency>

版本信 息:<junit.version>4.11</junit.version>、<hamcrest.core.version>1.3< /hamcrest.core.version>、<mockito.core.version>1.9.5< /mockito.core.version>

然后准备测试相关配置

实体:

1
2
3
4
5
6
7
package  com.sishuok.mvc.entity;
import  java.io.Serializable;
public  class  User  implements  Serializable {
     private  Long id;
     private  String name;
     //省略getter/setter等
}

控制器:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package  com.sishuok.mvc.controller;
//省略import
@Controller
@RequestMapping ( "/user" )
public  class  UserController {
 
     @RequestMapping ( "/{id}" )
     public  ModelAndView view( @PathVariable ( "id" ) Long id, HttpServletRequest req) {
         User user =  new  User();
         user.setId(id);
         user.setName( "zhang" );
 
         ModelAndView mv =  new  ModelAndView();
         mv.addObject( "user" , user);
         mv.setViewName( "user/view" );
         return  mv;
     }
}

XML风格配置:

spring-config.xml:加载非web层组件 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?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: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/context http://www.springframework.org/schema/context/spring-context.xsd
        ">
     <!-- 通过web.xml中的 org.springframework.web.context.ContextLoaderListener 加载的  -->
     <!-- 请参考 http: //jinnianshilongnian.iteye.com/blog/1602617  -->
<context:component-scan base- package = "com.sishuok.mvc" >
     <context:exclude-filter type= "annotation"  expression= "org.springframework.stereotype.Controller" />
</context:component-scan>
</beans>

spring-mvc.xml:加载和配置web层组件 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?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:context= "http://www.springframework.org/schema/context"
        xmlns:mvc= "http://www.springframework.org/schema/mvc"
        xsi:schemaLocation="
        http: //www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http: //www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
        http: //www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
        ">
     <!-- 通过web.xml中的 org.springframework.web.servlet.DispatcherServlet 加载的  -->
     <!-- 请参考 http: //jinnianshilongnian.iteye.com/blog/1602617  -->
     <context:component-scan base- package = "com.sishuok.mvc"  use- default -filters= "false" >
         <context:include-filter type= "annotation"  expression= "org.springframework.stereotype.Controller" />
     </context:component-scan>
     <mvc:annotation-driven/>
     <bean id= "viewResolver"  class = "org.springframework.web.servlet.view.InternalResourceViewResolver" >
         <property name= "prefix"  value= "/WEB-INF/jsp/" />
         <property name= "suffix"  value= ".jsp" />
     </bean>
</beans>

web.xml配置:此处就不贴了,请前往github查看。

 

对于context:component-scan注意事项请参考《context:component-scan扫描使用上的容易忽略的use-default-filters》和《第三章 DispatcherServlet详解 ——跟开涛学SpringMVC》。

 

等价的注解风格配置: 

AppConfig.java:等价于spring-config.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
package  com.sishuok.config;
 
import  org.springframework.context.annotation.ComponentScan;
import  org.springframework.context.annotation.Configuration;
import  org.springframework.context.annotation.FilterType;
import  org.springframework.stereotype.Controller;
 
@Configuration
@ComponentScan (basePackages =  "com.sishuok.mvc" , excludeFilters = {
         @ComponentScan .Filter(type = FilterType.ANNOTATION, value = {Controller. class })
})
public  class  AppConfig {
}

MvcConfig.java:等价于spring-mvc.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package  com.sishuok.config;
 
import  org.springframework.context.annotation.Bean;
import  org.springframework.context.annotation.ComponentScan;
import  org.springframework.context.annotation.Configuration;
import  org.springframework.context.annotation.FilterType;
import  org.springframework.stereotype.Controller;
import  org.springframework.web.servlet.ViewResolver;
import  org.springframework.web.servlet.config.annotation.EnableWebMvc;
import  org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
import  org.springframework.web.servlet.view.InternalResourceViewResolver;
 
@Configuration
@EnableWebMvc
@ComponentScan (basePackages =  "com.sishuok.mvc" , useDefaultFilters =  false , includeFilters = {
         @ComponentScan .Filter(type = FilterType.ANNOTATION, value = {Controller. class })
})
public  class  MvcConfig  extends  WebMvcConfigurationSupport {
 
     @Bean
     public  ViewResolver viewResolver() {
         InternalResourceViewResolver viewResolver =  new  InternalResourceViewResolver();
         viewResolver.setPrefix( "/WEB-INF/jsp/" );
         viewResolver.setSuffix( ".jsp" );
         return  viewResolver;
     }
 
}

WebInitializer.java:注册相应的web.xml中的组件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
package  com.sishuok.config;
 
import  org.springframework.web.WebApplicationInitializer;
import  org.springframework.web.context.ContextLoaderListener;
import  org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import  org.springframework.web.filter.CharacterEncodingFilter;
import  org.springframework.web.servlet.DispatcherServlet;
 
import  javax.servlet.DispatcherType;
import  javax.servlet.FilterRegistration;
import  javax.servlet.ServletException;
import  javax.servlet.ServletRegistration;
import  java.util.EnumSet;
 
public  class  WebInitializer  implements  WebApplicationInitializer {
 
     @Override
     public  void  onStartup(javax.servlet.ServletContext sc)  throws  ServletException {
 
         AnnotationConfigWebApplicationContext rootContext =  new  AnnotationConfigWebApplicationContext();
         rootContext.register(AppConfig. class );
         sc.addListener( new  ContextLoaderListener(rootContext));
 
         //2、springmvc上下文
         AnnotationConfigWebApplicationContext springMvcContext =  new  AnnotationConfigWebApplicationContext();
         springMvcContext.register(MvcConfig. class );
         //3、DispatcherServlet
         DispatcherServlet dispatcherServlet =  new  DispatcherServlet(springMvcContext);
         ServletRegistration.Dynamic dynamic = sc.addServlet( "dispatcherServlet" , dispatcherServlet);
         dynamic.setLoadOnStartup( 1 );
         dynamic.addMapping( "/" );
 
         //4、CharacterEncodingFilter
         CharacterEncodingFilter characterEncodingFilter =  new  CharacterEncodingFilter();
         characterEncodingFilter.setEncoding( "utf-8" );
         FilterRegistration filterRegistration =
                 sc.addFilter( "characterEncodingFilter" , characterEncodingFilter);
         filterRegistration.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST),  false "/" );
 
     }
}

对于WebInitializer,请参考《Spring4新特性——Groovy Bean定义DSL

到此基本的配置就搞定了,接下来看看如何测试吧。 

 

1.1 以前的测试方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package  com.sishuok.mvc.controller;
//省略import
public  class  UserControllerTest {
 
     private  UserController userController;
 
     @Before
     public  void  setUp() {
         userController =  new  UserController();
         //安装userCtroller依赖 比如userService
     }
 
     @Test
     public  void  testView() {
         MockHttpServletRequest req =  new  MockHttpServletRequest();
         ModelAndView mv = userController.view(1L, req);
 
         ModelAndViewAssert.assertViewName(mv,  "user/view" );
         ModelAndViewAssert.assertModelAttributeAvailable(mv,  "user" );
 
     }
}

准备控制器:我们通过new方式创建一个,然后手工查找依赖注入进去(比如从spring容器获取/new的);

Mock Request:此处使用Spring提供的Mock API模拟一个HttpServletRequest,其他的Servlet API也提供了相应的Mock类,具体请查看Javadoc;

访问控制器方法:通过直接调用控制器方法进行访问,此处无法验证Spring MVC框架的类型转换、数据验证等是否正常;

ModelAndViewAssert:通过这个Assert API验证我们的返回值是否正常;

 

对于单元测试步骤请参考:加速Java应用开发速度3——单元/集成测试+CI 

 

这种方式的缺点已经说过了,如不能走Spring MVC完整流程(不能走Servlet的过滤器链、SpringMVC的类型转换、数据验证、数据绑定、拦截器等等),如果做基本的测试没问题,这种方式 就是纯粹的单元测试,我们想要的功能其实是一种集成测试,不过后续部分不区分。

 

1.2 安装测试环境

spring mvc测试框架提供了两种方式,独立安装和集成Web环境测试(此种方式并不会集成真正的web环境,而是通过相应的Mock API进行模拟测试,无须启动服务器)。

 

独立测试方式

1
2
3
4
5
6
7
8
public  class  UserControllerStandaloneSetupTest {
     private  MockMvc mockMvc;
     @Before
     public  void  setUp() {
         UserController userController =  new  UserController();
         mockMvc = MockMvcBuilders.standaloneSetup(userController).build();
     }
}

1、首先自己创建相应的控制器,注入相应的依赖

2、通过MockMvcBuilders.standaloneSetup模拟一个Mvc测试环境,通过build得到一个MockMvc

3、MockMvc:是我们以后测试时经常使用的API,后边介绍

 

集成Web环境方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
//XML风格
@RunWith (SpringJUnit4ClassRunner. class )
@WebAppConfiguration (value =  "src/main/webapp" )
@ContextHierarchy ({
         @ContextConfiguration (name =  "parent" , locations =  "classpath:spring-config.xml" ),
         @ContextConfiguration (name =  "child" , locations =  "classpath:spring-mvc.xml" )
})
 
//注解风格
//@RunWith(SpringJUnit4ClassRunner.class)
//@WebAppConfiguration(value = "src/main/webapp")
//@ContextHierarchy({
//        @ContextConfiguration(name = "parent", classes = AppConfig.class),
//        @ContextConfiguration(name = "child", classes = MvcConfig.class)
//})
public  class  UserControllerWebAppContextSetupTest {
 
     @Autowired
     private  WebApplicationContext wac;
     private  MockMvc mockMvc;
 
     @Before
     public  void  setUp() {
         mockMvc = MockMvcBuilders.webAppContextSetup(wac).build();
     }
}

1、@WebAppConfiguration:测试环境使用,用来表示测试环境使用的ApplicationContext将是WebApplicationContext类型的;value指定web应用的根;

2、@ContextHierarchy:指定容器层次,即spring-config.xml是父容器,而spring-mvc.xml是子容器,请参考《第三章 DispatcherServlet详解 ——跟开涛学SpringMVC

3、通过@Autowired WebApplicationContext wac:注入web环境的ApplicationContext容器;

4、然后通过MockMvcBuilders.webAppContextSetup(wac).build()创建一个MockMvc进行测试;

 

到此测试环境就搭建完成了,根据需要选择使用哪种方式即可。相关配置请前往github查看

 

1.3、HelloWorld

1
2
3
4
5
6
7
8
9
10
     @Test
     public  void  testView()  throws  Exception {
         MvcResult result = mockMvc.perform(MockMvcRequestBuilders.get( "/user/1" ))
                 .andExpect(MockMvcResultMatchers.view().name( "user/view" ))
                 .andExpect(MockMvcResultMatchers.model().attributeExists( "user" ))
                 .andDo(MockMvcResultHandlers.print())
                 .andReturn();
         
         Assert.assertNotNull(result.getModelAndView().getModel().get( "user" ));
     }

    
    }

1.5 测试示例

测试普通控制器 

1
2
3
4
5
6
7
         //测试普通控制器
         mockMvc.perform(get( "/user/{id}" 1 ))  //执行请求
                 .andExpect(model().attributeExists( "user" ))  //验证存储模型数据
                 .andExpect(view().name( "user/view" ))  //验证viewName
                 .andExpect(forwardedUrl( "/WEB-INF/jsp/user/view.jsp" )) //验证视图渲染时forward到的jsp
                 .andExpect(status().isOk()) //验证状态码
                 .andDo(print());  //输出MvcResult到控制台















本文转自yunlielai51CTO博客,原文链接:http://blog.51cto.com/4925054/1715777 ,如需转载请自行联系原作者
相关文章
|
6天前
|
前端开发 Java Spring
Spring MVC 是如何对对象参数进行校验的
【6月更文挑战第4天】对象参数校验是使用 SpringMVC 时常用的功能,这篇文章尝试分析了,Spring 是如何实现这一功能的。
19 5
|
1天前
|
前端开发 Java Spring
Spring MVC 请求处理流程
Spring MVC 请求处理流程
4 0
|
2天前
|
前端开发 Java 测试技术
Spring Boot单元测试
Spring Boot单元测试
12 2
|
4天前
|
JSON 前端开发 Java
Spring MVC 级联对象参数校验
【6月更文挑战第6天】在 Spring MVC 的使用过程中,我们会发现很多非常符合直觉的功能特性,但往往我们会习惯这种「被照顾得很好」的开发方式,依靠直觉去判断很多功能特性的用法。
9 1
|
7天前
|
XML 前端开发 Java
Spring3 MVC中使用Swagger生成API文档
Spring3 MVC中使用Swagger生成API文档
10 0
|
7天前
|
JSON 前端开发 API
Apache HttpClient调用Spring3 MVC Restful Web API演示
Apache HttpClient调用Spring3 MVC Restful Web API演示
11 1
|
7天前
|
前端开发 Java 关系型数据库
在Spring3 MVC中五步配置集成注解方式Hibernate3
在Spring3 MVC中五步配置集成注解方式Hibernate3
17 3
|
7天前
|
JSON 前端开发 Java
记录一次让我吐血的spring3 MVC HTTP406 Json转换错误
记录一次让我吐血的spring3 MVC HTTP406 Json转换错误
6 0
|
7天前
|
前端开发 Java Spring
自定义 Spring MVC Controller 方法参数处理
【6月更文挑战第3天】在 Spring MVC Controller 的方法参数,Spring 会自动为我们注入一些特殊的参数值,比如 HttpServletRequest、HttpServletResponse 等对象,或者 HTTP 请求参数。
48 0
|
8天前
|
前端开发 IDE Java
Spring3 MVC 集成Velocity中文支持
Spring3 MVC 集成Velocity中文支持
27 7