亚洲免费在线-亚洲免费在线播放-亚洲免费在线观看-亚洲免费在线观看视频-亚洲免费在线看-亚洲免费在线视频

Spring MVC測試框架詳解——服務端測試

系統 1932 0

隨著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依賴:

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

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

?

如下是測試相關的依賴(junit、hamcrest、mockito、spring-test):

Java代碼?? 收藏代碼
  1. <dependency>??
  2. ????<groupId>junit</groupId>??
  3. ????<artifactId>junit</artifactId>??
  4. ????<version>${junit.version}</version>??
  5. ????<scope>test</scope>??
  6. </dependency>??
  7. ??
  8. <dependency>??
  9. ????<groupId>org.hamcrest</groupId>??
  10. ????<artifactId>hamcrest-core</artifactId>??
  11. ????<version>${hamcrest.core.version}/version>??
  12. ????<scope>test</scope>??
  13. </dependency>??
  14. <dependency>??
  15. ????<groupId>org.mockito</groupId>??
  16. ????<artifactId>mockito-core</artifactId>??
  17. ????<version>${mockito.core.version}</version>??
  18. ????<scope>test</scope>??
  19. </dependency>??
  20. ??
  21. <dependency>??
  22. ????<groupId>org.springframework</groupId>??
  23. ????<artifactId>spring-test</artifactId>??
  24. ????<version>${spring.version}</version>??
  25. ????<scope>test</scope>??
  26. </dependency>??
版本信息:<junit.version>4.11</junit.version>、<hamcrest.core.version>1.3</hamcrest.core.version>、<mockito.core.version>1.9.5</mockito.core.version>
?

然后準備測試相關配置

實體:

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

?

控制器:

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

?

XML風格配置:

spring-config.xml :加載非web層組件 ?

Java代碼?? 收藏代碼
  1. <?xml?version= "1.0" ?encoding= "UTF-8" ?>??
  2. <beans?xmlns= "http://www.springframework.org/schema/beans" ??
  3. ???????xmlns:xsi= "http://www.w3.org/2001/XMLSchema-instance" ??
  4. ???????xmlns:context= "http://www.springframework.org/schema/context" ??
  5. ???????xsi:schemaLocation="??
  6. ???????http: //www.springframework.org/schema/beans?http://www.springframework.org/schema/beans/spring-beans.xsd ??
  7. ???????http: //www.springframework.org/schema/context?http://www.springframework.org/schema/context/spring-context.xsd ??
  8. ???????">??
  9. ????<!--?通過web.xml中的?org.springframework.web.context.ContextLoaderListener?加載的??-->??
  10. ????<!--?請參考?http: //jinnianshilongnian.iteye.com/blog/1602617??--> ??
  11. ????<context:component-scan?base- package = "com.sishuok.mvc" >??
  12. ????????<context:exclude-filter?type= "annotation" ?expression= "org.springframework.stereotype.Controller" />??
  13. ????</context:component-scan>??
  14. </beans>??

?

spring-mvc.xml :加載和配置web層組件 ?

Java代碼?? 收藏代碼
  1. <?xml?version= "1.0" ?encoding= "UTF-8" ?>??
  2. <beans?xmlns= "http://www.springframework.org/schema/beans" ??
  3. ???????xmlns:xsi= "http://www.w3.org/2001/XMLSchema-instance" ??
  4. ???????xmlns:context= "http://www.springframework.org/schema/context" ??
  5. ???????xmlns:mvc= "http://www.springframework.org/schema/mvc" ??
  6. ???????xsi:schemaLocation="??
  7. ???????http: //www.springframework.org/schema/beans?http://www.springframework.org/schema/beans/spring-beans.xsd ??
  8. ???????http: //www.springframework.org/schema/context?http://www.springframework.org/schema/context/spring-context.xsd ??
  9. ???????http: //www.springframework.org/schema/mvc?http://www.springframework.org/schema/mvc/spring-mvc.xsd ??
  10. ???????">??
  11. ????<!--?通過web.xml中的?org.springframework.web.servlet.DispatcherServlet?加載的??-->??
  12. ????<!--?請參考?http: //jinnianshilongnian.iteye.com/blog/1602617??--> ??
  13. ????<context:component-scan?base- package = "com.sishuok.mvc" ?use- default -filters= "false" >??
  14. ????????<context:include-filter?type= "annotation" ?expression= "org.springframework.stereotype.Controller" />??
  15. ????</context:component-scan>??
  16. ????<mvc:annotation-driven/>??
  17. ????<bean?id= "viewResolver" ? class = "org.springframework.web.servlet.view.InternalResourceViewResolver" >??
  18. ????????<property?name= "prefix" ?value= "/WEB-INF/jsp/" />??
  19. ????????<property?name= "suffix" ?value= ".jsp" />??
  20. ????</bean>??
  21. </beans>??

?

web.xml配置 :此處就不貼了,請前往github查看。

?

對于context:component-scan注意事項請參考《 context:component-scan掃描使用上的容易忽略的use-default-filters 》和《 第三章 DispatcherServlet詳解 ——跟開濤學SpringMVC 》。

?

等價的注解風格配置: ?

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

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

?

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

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

WebInitializer.java :注冊相應的web.xml中的組件

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

對于WebInitializer,請參考《 Spring4新特性——Groovy Bean定義DSL

到此基本的配置就搞定了,接下來看看如何測試吧。?

?

1.1 以前的測試方式

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

準備控制器 :我們通過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進行模擬測試,無須啟動服務器)。

?

獨立測試方式

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

1、首先自己創建相應的控制器,注入相應的依賴

2、通過MockMvcBuilders.standaloneSetup模擬一個Mvc測試環境,通過build得到一個MockMvc

3、MockMvc:是我們以后測試時經常使用的API,后邊介紹

?

集成Web環境方式

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

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

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

1、mockMvc.perform執行一個請求;

2、MockMvcRequestBuilders.get("/user/1")構造一個請求

3、ResultActions.andExpect添加執行完成后的斷言

4、ResultActions.andDo添加一個結果處理器,表示要對結果做點什么事情,比如此處使用MockMvcResultHandlers.print()輸出整個響應結果信息。

5、ResultActions.andReturn表示執行完成后返回相應的結果。

?

整個測試過程非常有規律:

1、準備測試環境

2、通過MockMvc執行請求

3.1、添加驗證斷言

3.2、添加結果處理器

3.3、得到MvcResult進行自定義斷言/進行下一步的異步請求

4、卸載測試環境

?

對于單元測試步驟請參考: 加速Java應用開發速度3——單元/集成測試+CI

?

1.4、了解測試API

Spring mvc測試框架提供了測試MVC需要的API,主要包括Servlet/JSP Mock、MockMvcBuilder、MockMvc、RequestBuilder、ResultMatcher、ResultHandler、MvcResult等。另外提供了幾個靜態工廠方法便于測試:MockMvcBuilders、MockMvcRequestBuilders、MockMvcResultMatchers、MockMvcResultHandlers。在使用時請使用靜態方法導入方便測試,如:

Java代碼?? 收藏代碼
  1. import ? static ?org.springframework.test.web.servlet.setup.MockMvcBuilders.*;??
  2. import ? static ?org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;??
  3. import ? static ?org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;??
  4. import ? static ?org.springframework.test.web.servlet.result.MockMvcResultHandlers.*;??

?

Servlet/JSP API Mock?

提供了對Servlet 3 相應API的Mock,如:

MockServletContext

MockHttpServletRequest

MockHttpServletResponse

……

具體請查看spring-test模塊的org.springframework.mock.web包。

?

?

MockMvcBuilder/MockMvcBuilders

MockMvcBuilder是用來構造MockMvc的構造器,其主要有兩個實現:StandaloneMockMvcBuilder和DefaultMockMvcBuilder,分別對應之前的兩種測試方式。對于我們來說直接使用靜態工廠MockMvcBuilders創建即可:

MockMvcBuilders.webAppContextSetup(WebApplicationContext context):指定WebApplicationContext,將會從該上下文獲取相應的控制器并得到相應的MockMvc;

MockMvcBuilders.standaloneSetup(Object... controllers):通過參數指定一組控制器,這樣就不需要從上下文獲取了;

?

其中DefaultMockMvcBuilder還提供了如下API:

addFilters(Filter... filters)/addFilter(Filter filter, String... urlPatterns):添加javax.servlet.Filter過濾器

defaultRequest(RequestBuilder requestBuilder):默認的RequestBuilder,每次執行時會合并到自定義的RequestBuilder中,即提供公共請求數據的;

alwaysExpect(ResultMatcher resultMatcher):定義全局的結果驗證器,即每次執行請求時都進行驗證的規則;

alwaysDo(ResultHandler resultHandler):定義全局結果處理器,即每次請求時都進行結果處理;

dispatchOptions:DispatcherServlet是否分發OPTIONS請求方法到控制器;

?

StandaloneMockMvcBuilder繼承了DefaultMockMvcBuilder,又提供了如下API:

setMessageConverters(HttpMessageConverter<?>...messageConverters):設置HTTP消息轉換器;

setValidator(Validator validator):設置驗證器;

setConversionService(FormattingConversionService conversionService):設置轉換服務;

addInterceptors(HandlerInterceptor... interceptors)/addMappedInterceptors(String[] pathPatterns, HandlerInterceptor... interceptors):添加spring mvc攔截器;

setContentNegotiationManager(ContentNegotiationManager contentNegotiationManager):設置內容協商管理器;

setAsyncRequestTimeout(long timeout):設置異步超時時間;

setCustomArgumentResolvers(HandlerMethodArgumentResolver... argumentResolvers):設置自定義控制器方法參數解析器;

setCustomReturnValueHandlers(HandlerMethodReturnValueHandler... handlers):設置自定義控制器方法返回值處理器;

setHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers)/setHandlerExceptionResolvers(HandlerExceptionResolver... exceptionResolvers):設置異常解析器;

setViewResolvers(ViewResolver...resolvers):設置視圖解析器;

setSingleView(View view):設置單個視圖,即視圖解析時總是解析到這一個(僅適用于只有一個視圖的情況);

setLocaleResolver(LocaleResolver localeResolver):設置Local解析器;

setFlashMapManager(FlashMapManager flashMapManager):設置FlashMapManager,如存儲重定向數據;

setUseSuffixPatternMatch(boolean useSuffixPatternMatch):設置是否是后綴模式匹配,如“/user”是否匹配"/user.*",默認真即匹配;

setUseTrailingSlashPatternMatch(boolean useTrailingSlashPatternMatch):設置是否自動后綴路徑模式匹配,如“/user”是否匹配“/user/”,默認真即匹配;

addPlaceHolderValue(String name, String value) :添加request mapping中的占位符替代;

?

因為StandaloneMockMvcBuilder不會加載Spring MVC配置文件,因此就不會注冊我們需要的一些組件,因此就提供了如上API用于注冊我們需要的相應組件。

?

MockMvc

使用之前的MockMvcBuilder.build()得到構建好的MockMvc;這個是mvc測試的核心API,對于該API的使用方式如下:

Java代碼?? 收藏代碼
  1. MvcResult?result?=?mockMvc.perform(MockMvcRequestBuilders.get( "/user/1" ))??
  2. ???????.andExpect(MockMvcResultMatchers.view().name( "user/view" ))??
  3. ???????.andExpect(MockMvcResultMatchers.model().attributeExists( "user" ))??
  4. ???????.andDo(MockMvcResultHandlers.print())??
  5. ???????.andReturn();??

perform:執行一個RequestBuilder請求,會自動執行SpringMVC的流程并映射到相應的控制器執行處理;

andExpect:添加ResultMatcher驗證規則,驗證控制器執行完成后結果是否正確;

andDo:添加ResultHandler結果處理器,比如調試時打印結果到控制臺;

andReturn:最后返回相應的MvcResult;然后進行自定義驗證/進行下一步的異步處理;

?

另外還提供了以下API:

setDefaultRequest:設置默認的RequestBuilder,用于在每次perform執行相應的RequestBuilder時自動把該默認的RequestBuilder合并到perform的RequestBuilder中;

setGlobalResultMatchers:設置全局的預期結果驗證規則,如我們通過MockMvc測試多個控制器時,假設它們都想驗證某個規則時,就可以使用這個;

setGlobalResultHandlers:設置全局的ResultHandler結果處理器;

??

RequestBuilder/MockMvcRequestBuilders

從名字可以看出,RequestBuilder用來構建請求的,其提供了一個方法buildRequest(ServletContext servletContext)用于構建MockHttpServletRequest;其主要有兩個子類MockHttpServletRequestBuilder和MockMultipartHttpServletRequestBuilder(如 文件上傳 使用),即用來Mock客戶端請求需要的所有數據。

?

MockMvcRequestBuilders主要API:

MockHttpServletRequestBuilder get(String urlTemplate, Object... urlVariables):根據uri模板和uri變量值得到一個GET請求方式的MockHttpServletRequestBuilder;如get("/user/{id}", 1L);

MockHttpServletRequestBuilder post(String urlTemplate, Object... urlVariables):同get類似,但是是POST方法;

MockHttpServletRequestBuilder put(String urlTemplate, Object... urlVariables):同get類似,但是是PUT方法;

MockHttpServletRequestBuilder delete(String urlTemplate, Object... urlVariables) :同get類似,但是是DELETE方法;

MockHttpServletRequestBuilder options(String urlTemplate, Object... urlVariables):同get類似,但是是OPTIONS方法;

MockHttpServletRequestBuilder request(HttpMethod httpMethod, String urlTemplate, Object... urlVariables):提供自己的Http請求方法及uri模板和uri變量,如上API都是委托給這個API;

MockMultipartHttpServletRequestBuilder fileUpload(String urlTemplate, Object... urlVariables):提供 文件上傳 方式的請求,得到MockMultipartHttpServletRequestBuilder;

RequestBuilder asyncDispatch(final MvcResult mvcResult):創建一個從啟動異步處理的請求的MvcResult進行異步分派的RequestBuilder;

?

接下來再看看MockHttpServletRequestBuilder和MockMultipartHttpServletRequestBuilder API:

MockHttpServletRequestBuilder API:

MockHttpServletRequestBuilder header(String name, Object... values)/MockHttpServletRequestBuilder headers(HttpHeaders httpHeaders):添加頭信息;

MockHttpServletRequestBuilder contentType(MediaType mediaType):指定請求的contentType頭信息;

MockHttpServletRequestBuilder accept(MediaType... mediaTypes)/MockHttpServletRequestBuilder accept(String... mediaTypes):指定請求的Accept頭信息;

MockHttpServletRequestBuilder content(byte[] content)/MockHttpServletRequestBuilder content(String content):指定請求Body體內容;

MockHttpServletRequestBuilder cookie(Cookie... cookies):指定請求的Cookie;

MockHttpServletRequestBuilder locale(Locale locale):指定請求的Locale;

MockHttpServletRequestBuilder characterEncoding(String encoding):指定請求字符編碼;

MockHttpServletRequestBuilder requestAttr(String name, Object value) :設置請求屬性數據;

MockHttpServletRequestBuilder sessionAttr(String name, Object value)/MockHttpServletRequestBuilder sessionAttrs(Map<String, Object> sessionAttributes):設置請求session屬性數據;

MockHttpServletRequestBuilder flashAttr(String name, Object value)/MockHttpServletRequestBuilder flashAttrs(Map<String, Object> flashAttributes):指定請求的flash信息,比如重定向后的屬性信息;

MockHttpServletRequestBuilder session(MockHttpSession session) :指定請求的Session;

MockHttpServletRequestBuilder principal(Principal principal) :指定請求的Principal;

MockHttpServletRequestBuilder contextPath(String contextPath) :指定請求的上下文路徑,必須以“/”開頭,且不能以“/”結尾;

MockHttpServletRequestBuilder pathInfo(String pathInfo) :請求的路徑信息,必須以“/”開頭;

MockHttpServletRequestBuilder secure(boolean secure):請求是否使用安全通道;

MockHttpServletRequestBuilder with(RequestPostProcessor postProcessor):請求的后處理器,用于自定義一些請求處理的擴展點;

?

MockMultipartHttpServletRequestBuilder繼承自MockHttpServletRequestBuilder,又提供了如下API:

MockMultipartHttpServletRequestBuilder file(String name, byte[] content)/MockMultipartHttpServletRequestBuilder file(MockMultipartFile file):指定要上傳的文件;

?

ResultActions

調用MockMvc.perform(RequestBuilder requestBuilder)后將得到ResultActions,通過ResultActions完成如下三件事:

ResultActions andExpect(ResultMatcher matcher) :添加驗證斷言來判斷執行請求后的結果是否是預期的;

ResultActions andDo(ResultHandler handler) :添加結果處理器,用于對驗證成功后執行的動作,如輸出下請求/結果信息用于調試;

MvcResult andReturn() :返回驗證成功后的MvcResult;用于自定義驗證/下一步的異步處理;

?

ResultMatcher/MockMvcResultMatchers

ResultMatcher用來匹配執行完請求后的結果驗證,其就一個match(MvcResult result)斷言方法,如果匹配失敗將拋出相應的異常;spring mvc測試框架提供了很多***ResultMatchers來滿足測試需求。注意這些***ResultMatchers并不是ResultMatcher的子類,而是返回ResultMatcher實例的。Spring mvc測試框架為了測試方便提供了MockMvcResultMatchers靜態工廠方法方便操作;具體的API如下:

HandlerResultMatchers handler():請求的Handler驗證器,比如驗證處理器類型/方法名;此處的Handler其實就是處理請求的控制器;

RequestResultMatchers request():得到RequestResultMatchers驗證器;

ModelResultMatchers model():得到模型驗證器;

ViewResultMatchers view():得到視圖驗證器;

FlashAttributeResultMatchers flash():得到Flash屬性驗證;

StatusResultMatchers status():得到響應狀態驗證器;

HeaderResultMatchers header():得到響應Header驗證器;

CookieResultMatchers cookie():得到響應Cookie驗證器;

ContentResultMatchers content():得到響應內容驗證器;

JsonPathResultMatchers jsonPath(String expression, Object ... args)/ResultMatcher jsonPath(String expression, Matcher<T> matcher):得到Json表達式驗證器;

XpathResultMatchers xpath(String expression, Object... args)/XpathResultMatchers xpath(String expression, Map<String, String> namespaces, Object... args):得到Xpath表達式驗證器;

ResultMatcher forwardedUrl(final String expectedUrl):驗證處理完請求后轉發的url(絕對匹配);

ResultMatcher forwardedUrlPattern(final String urlPattern):驗證處理完請求后轉發的url(Ant風格模式匹配,@since spring4);

ResultMatcher redirectedUrl(final String expectedUrl):驗證處理完請求后重定向的url(絕對匹配);

ResultMatcher redirectedUrlPattern(final String expectedUrl):驗證處理完請求后重定向的url(Ant風格模式匹配,@since spring4);

?

得到相應的***ResultMatchers后,接著再調用其相應的API得到ResultMatcher,如ModelResultMatchers.attributeExists(final String... names)判斷Model屬性是否存在。具體請查看相應的API。再次就不一一列舉了。

?

?

ResultHandler/MockMvcResultHandlers

ResultHandler用于對處理的結果進行相應處理的,比如輸出整個請求/響應等信息方便調試,Spring mvc測試框架提供了MockMvcResultHandlers靜態工廠方法,該工廠提供了ResultHandler print()返回一個輸出MvcResult詳細信息到控制臺的ResultHandler實現。

?

?

MvcResult

即執行完控制器后得到的整個結果,并不僅僅是返回值,其包含了測試時需要的所有信息,如:

MockHttpServletRequest getRequest():得到執行的請求;

MockHttpServletResponse getResponse():得到執行后的響應;

Object getHandler():得到執行的處理器,一般就是控制器;

HandlerInterceptor[] getInterceptors():得到對處理器進行攔截的攔截器;

ModelAndView getModelAndView():得到執行后的ModelAndView;

Exception getResolvedException():得到HandlerExceptionResolver解析后的異常;

FlashMap getFlashMap():得到FlashMap;

Object getAsyncResult()/Object getAsyncResult(long timeout):得到異步執行的結果;

?

1.5 測試示例

測試普通控制器?

Java代碼?? 收藏代碼
  1. //測試普通控制器 ??
  2. mockMvc.perform(get( "/user/{id}" ,? 1 ))? //執行請求 ??
  3. ????????.andExpect(model().attributeExists( "user" ))? //驗證存儲模型數據 ??
  4. ????????.andExpect(view().name( "user/view" ))? //驗證viewName ??
  5. ????????.andExpect(forwardedUrl( "/WEB-INF/jsp/user/view.jsp" )) //驗證視圖渲染時forward到的jsp ??
  6. ????????.andExpect(status().isOk()) //驗證狀態碼 ??
  7. ????????.andDo(print());? //輸出MvcResult到控制臺 ??

?

測試普通控制器,但是URL錯誤,即404

Java代碼?? 收藏代碼
  1. //找不到控制器,404測試 ??
  2. MvcResult?result?=?mockMvc.perform(get( "/user2/{id}" ,? 1 ))? //執行請求 ??
  3. ????????.andDo(print())??
  4. ????????.andExpect(status().isNotFound())? //驗證控制器不存在 ??
  5. ????????.andReturn();??
  6. Assert.assertNull(result.getModelAndView());? //自定義斷言 ??

?

得到MvcResult自定義驗證 ???

Java代碼?? 收藏代碼
  1. MvcResult?result?=?mockMvc.perform(get( "/user/{id}" ,? 1 )) //執行請求 ??
  2. ????????.andReturn();? //返回MvcResult ??
  3. Assert.assertNotNull(result.getModelAndView().getModel().get( "user" ));? //自定義斷言 ??

?

驗證請求參數綁定到模型數據及Flash屬性?

Java代碼?? 收藏代碼
  1. mockMvc.perform(post( "/user" ).param( "name" ,? "zhang" ))? //執行傳遞參數的POST請求(也可以post("/user?name=zhang")) ??
  2. ????????.andExpect(handler().handlerType(UserController. class ))? //驗證執行的控制器類型 ??
  3. ????????.andExpect(handler().methodName( "create" ))? //驗證執行的控制器方法名 ??
  4. ????????.andExpect(model().hasNoErrors())? //驗證頁面沒有錯誤 ??
  5. ????????.andExpect(flash().attributeExists( "success" ))? //驗證存在flash屬性 ??
  6. ????????.andExpect(view().name( "redirect:/user" ));? //驗證視圖 ??

?

驗證請求參數驗證失敗出錯??

Java代碼?? 收藏代碼
  1. mockMvc.perform(post( "/user" ).param( "name" ,? "admin" ))? //執行請求 ??
  2. ????????.andExpect(model().hasErrors())? //驗證模型有錯誤 ??
  3. ????????.andExpect(model().attributeDoesNotExist( "name" ))? //驗證存在錯誤的屬性 ??
  4. ????????.andExpect(view().name( "showCreateForm" ));? //驗證視圖 ??

?

文件上傳 ?

Java代碼?? 收藏代碼
  1. // 文件上傳 ??
  2. byte []?bytes?=? new ? byte []?{ 1 ,? 2 };??
  3. mockMvc.perform(fileUpload( "/user/{id}/icon" ,?1L).file( "icon" ,?bytes))? //執行 文件上傳 ??
  4. ????????.andExpect(model().attribute( "icon" ,?bytes))? //驗證屬性相等性 ??
  5. ????????.andExpect(view().name( "success" ));? //驗證視圖 ??

?

JSON請求/響應驗證

測試時需要安裝jackson Json和JsonPath依賴:?

Java代碼?? 收藏代碼
  1. <dependency>??
  2. ????<groupId>com.fasterxml.jackson.core</groupId>??
  3. ????<artifactId>jackson-databind</artifactId>??
  4. ????<version>${jackson2.version}</version>??
  5. </dependency>??
  6. ??
  7. <dependency>??
  8. ????<groupId>com.jayway.jsonpath</groupId>??
  9. ????<artifactId>json-path</artifactId>??
  10. ????<version>${jsonpath.version}</version>??
  11. ????<scope>test</scope>??
  12. </dependency>??
版本:<jsonpath.version>0.9.0</jsonpath.version>、<jackson2.version>2.2.3</jackson2.version>?
Java代碼?? 收藏代碼
  1. String?requestBody?=? "{\"id\":1,?\"name\":\"zhang\"}" ;??
  2. mockMvc.perform(post( "/user" )??
  3. ????????????.contentType(MediaType.APPLICATION_JSON).content(requestBody)??
  4. ????????????.accept(MediaType.APPLICATION_JSON))? //執行請求 ??
  5. ????????.andExpect(content().contentType(MediaType.APPLICATION_JSON))? //驗證響應contentType ??
  6. ????????.andExpect(jsonPath( "$.id" ).value( 1 ));? //使用Json?path驗證JSON?請參考http://goessner.net/articles/JsonPath/ ??
  7. ??
  8. String?errorBody?=? "{id:1,?name:zhang}" ;??
  9. MvcResult?result?=?mockMvc.perform(post( "/user" )??
  10. ????????.contentType(MediaType.APPLICATION_JSON).content(errorBody)??
  11. ????????.accept(MediaType.APPLICATION_JSON))? //執行請求 ??
  12. ????????.andExpect(status().isBadRequest())? //400錯誤請求 ??
  13. ????????.andReturn();??
  14. ??
  15. Assert.assertTrue(HttpMessageNotReadableException. class .isAssignableFrom(result.getResolvedException().getClass())); //錯誤的請求內容體 ??

?

XML請求/響應驗證

測試時需要安裝spring oxm和xstream依賴:?

Java代碼?? 收藏代碼
  1. <dependency>??
  2. ????<groupId>com.thoughtworks.xstream</groupId>??
  3. ????<artifactId>xstream</artifactId>??
  4. ????<version>${xsream.version}</version>??
  5. ????<scope>test</scope>??
  6. </dependency>??
  7. ??
  8. <dependency>??
  9. ????<groupId>org.springframework</groupId>??
  10. ????<artifactId>spring-oxm</artifactId>??
  11. ????<version>${spring.version}</version>??
  12. ????<scope>test</scope>??
  13. </dependency>??
版本:<xstream.version>1.4.4</xstream.version>
Java代碼?? 收藏代碼
  1. //XML請求/響應 ??
  2. String?requestBody?=? "<user><id>1</id><name>zhang</name></user>" ;??
  3. mockMvc.perform(post( "/user" )??
  4. ????????.contentType(MediaType.APPLICATION_XML).content(requestBody)??
  5. ????????.accept(MediaType.APPLICATION_XML))? //執行請求 ??
  6. ????????.andDo(print())??
  7. ????????.andExpect(content().contentType(MediaType.APPLICATION_XML))? //驗證響應contentType ??
  8. ????????.andExpect(xpath( "/user/id/text()" ).string( "1" ));? //使用XPath表達式驗證XML?請參考http://www.w3school.com.cn/xpath/ ??
  9. ??
  10. String?errorBody?=? "<user><id>1</id><name>zhang</name>" ;??
  11. MvcResult?result?=?mockMvc.perform(post( "/user" )??
  12. ????????.contentType(MediaType.APPLICATION_XML).content(errorBody)??
  13. ????????.accept(MediaType.APPLICATION_XML))? //執行請求 ??
  14. ????????.andExpect(status().isBadRequest())? //400錯誤請求 ??
  15. ????????.andReturn();??
  16. ??
  17. Assert.assertTrue(HttpMessageNotReadableException. class .isAssignableFrom(result.getResolvedException().getClass())); //錯誤的請求內容體 ??

?

異常處理??

Java代碼?? 收藏代碼
  1. //異常處理 ??
  2. MvcResult?result?=?mockMvc.perform(get( "/user/exception" ))? //執行請求 ??
  3. ????????.andExpect(status().isInternalServerError())? //驗證服務器內部錯誤 ??
  4. ????????.andReturn();??
  5. ??
  6. Assert.assertTrue(IllegalArgumentException. class .isAssignableFrom(result.getResolvedException().getClass()));??

?

靜態資源?

Java代碼?? 收藏代碼
  1. //靜態資源 ??
  2. mockMvc.perform(get( "/static/app.js" ))? //執行請求 ??
  3. ????????.andExpect(status().isOk())? //驗證狀態碼200 ??
  4. ????????.andExpect(content().string(CoreMatchers.containsString( "var" ))); //驗證渲染后的視圖內容包含var ??
  5. ??
  6. mockMvc.perform(get( "/static/app1.js" ))? //執行請求 ??
  7. ????????.andExpect(status().isNotFound());?? //驗證狀態碼404 ??

異步測試?

Java代碼?? 收藏代碼
  1. //Callable ??
  2. MvcResult?result?=?mockMvc.perform(get( "/user/async1?id=1&name=zhang" ))? //執行請求 ??
  3. ????????.andExpect(request().asyncStarted())??
  4. ????????.andExpect(request().asyncResult(CoreMatchers.instanceOf(User. class )))? //默認會等10秒超時 ??
  5. ????????.andReturn();??
  6. ??
  7. mockMvc.perform(asyncDispatch(result))??
  8. ????????.andExpect(status().isOk())??
  9. ????????.andExpect(content().contentType(MediaType.APPLICATION_JSON))??
  10. ????????.andExpect(jsonPath( "$.id" ).value( 1 ));??
Java代碼?? 收藏代碼
  1. //DeferredResult ??
  2. result?=?mockMvc.perform(get( "/user/async2?id=1&name=zhang" ))? //執行請求 ??
  3. ????????.andExpect(request().asyncStarted())??
  4. ????????.andExpect(request().asyncResult(CoreMatchers.instanceOf(User. class )))?? //默認會等10秒超時 ??
  5. ????????.andReturn();??
  6. ??
  7. mockMvc.perform(asyncDispatch(result))??
  8. ????????.andExpect(status().isOk())??
  9. ????????.andExpect(content().contentType(MediaType.APPLICATION_JSON))??
  10. ????????.andExpect(jsonPath( "$.id" ).value( 1 ));??

此處請在第一次請求時加上 andExpect(request().asyncResult(CoreMatchers.instanceOf(User.class)))這樣會等待結果返回/超時,無須自己設置線程等待了;此處注意request().asyncResult一定是在第一次請求發出;然后第二次通過asyncDispatch進行異步請求。

?

添加自定義過濾器

Java代碼?? 收藏代碼
  1. mockMvc?=?webAppContextSetup(wac).addFilter( new ?MyFilter(),? "/*" ).build();??
  2. mockMvc.perform(get( "/user/1" ))??
  3. ????????.andExpect(request().attribute( "filter" ,? true ));??

?

全局配置?

Java代碼?? 收藏代碼
  1. mockMvc?=?webAppContextSetup(wac)??
  2. ????????.defaultRequest(get( "/user/1" ).requestAttr( "default" ,? true ))? //默認請求?如果其是Mergeable類型的,會自動合并的哦mockMvc.perform中的RequestBuilder ??
  3. ????????.alwaysDo(print())?? //默認每次執行請求后都做的動作 ??
  4. ????????.alwaysExpect(request().attribute( "default" ,? true ))? //默認每次執行后進行驗證的斷言 ??
  5. ????????.build();??
  6. ??
  7. mockMvc.perform(get( "/user/1" ))??
  8. ????????.andExpect(model().attributeExists( "user" ));??

?

以上代碼請參考 我的github 。更多參考示例請 前往Spring github

?

?

只要記住測試步驟,按照步驟操作,整個測試過程是非常容易理解的:

1、準備測試環境

2、通過MockMvc執行請求

3.1、添加驗證斷言

3.2、添加結果處理器

3.3、得到MvcResult進行自定義斷言/進行下一步的異步請求

4、卸載測試環境

Spring MVC測試框架詳解——服務端測試


更多文章、技術交流、商務合作、聯系博主

微信掃碼或搜索:z360901061

微信掃一掃加我為好友

QQ號聯系: 360901061

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。

【本文對您有幫助就好】

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描上面二維碼支持博主2元、5元、10元、自定義金額等您想捐的金額吧,站長會非常 感謝您的哦!!!

發表我的評論
最新評論 總共0條評論
主站蜘蛛池模板: 99热在线精品观看 | 青青国产成人久久激情911 | 久草热草 | 国产成人91一区二区三区 | 久久精品这里热有精品 | 国产xxxx69免费大片 | 成人a级特黄毛片 | 91精品免费不卡在线观看 | 一区二区国产在线观看 | 91在线品视觉盛宴免费 | 真人午夜a一级毛片 | 曰本色wa | 91免费网站在线看入口黄 | 免费国产成人午夜在线观看 | 国产亚洲精品激情一区二区三区 | 精品久久久久久久久免费影院 | 欧美激情_区二区三区 | 亚洲精品亚洲人成人网 | 黑人一级毛片 | 精品影视 | 四虎久久影院 | 日日撸夜夜干 | 日韩精品欧美国产精品亚 | 日韩一区视频在线 | 四虎色播| 97视频 | 天堂va亚洲va欧美va国产 | 亚洲一区小说区中文字幕 | 日韩免费一级毛片欧美一级日韩片 | 三a大片| 中文字幕亚洲综合久久 | 国产成人精品曰本亚洲78 | 99热这里只有精品国产免费 | 日韩精品亚洲人成在线播放 | 亚洲一区二区三区中文字幕 | 色柚视频 | 国产精品福利视频手机免费观看 | 国产一级特黄aa毛片 | 国产视频自拍一区 | 国产午夜精品一区二区 | 狠色狠狠色狠狠狠色综合久久 |