|
|
|
|
|
|
创建mock对象不能对
final
,
Anonymous
(匿名类)
,
primitive
(基本数据类型如int、double等和包装类)
类进行mock。
如果mock的话,会给你一个红灯:
|
|
|
|
|
|
|
|
注意 :如果使用参数匹配器,那么所有的参数都要使用参数匹配器,不管是stubbing还是verify的时候都一样。
真的是好强大
Spring与单元测试
添加依赖
12345678 // https://mvnrepository.com/artifact/junit/junitcompile group: 'junit', name: 'junit', version: '4.12'// https://mvnrepository.com/artifact/org.mockito/mockito-corecompile group: 'org.mockito', name: 'mockito-core', version: '2.0.106-beta'//https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-testcompile group: 'org.springframework.boot', name: 'spring-boot-starter-test', version: '1.4.0.RELEASE'- 如果你要用到一些 Spring自带的注解 ,比如
@Autowired
的话,最好是在测试类的基类中,加入如下注解,这样会使得测试时先将 SpringBoot 运行起来。
@SpringgApplicationConfigtation
注解在 SpringBoot 的 1.4 版本以后不建议使用使用
SpringBootTest
注解替代@SpringgApplicationConfigtation
123 @RunWith(SpringJUnit4ClassRunner.class)@WebAppConfiguration@SpringBootTest(classes = Application.class)- 接下来需要在@Before注解的setUp()中进行初始化
12345678910 @RunWith(SpringJUnit4ClassRunner.class)@WebAppConfiguration@SpringBootTest(classes = Application.class)public abstract class MockitoBasedTest {@Beforepublic void setUp() throws Exception {// 初始化测试用例类中由Mockito的注解标注的所有模拟对象MockitoAnnotations.initMocks(this);}}被测类中
@Autowired
注解,如何控制其中Repository返回值
12345678910 public class GameHelper {@Autowiredprivate PointRepository pointRepository;public boolean checkLineItem(final Line line) {Point fromPoint = pointRepository.findById(line.getFromPointId()); //如何控制这个repository的返回?Point toPoint = pointRepository.findById(line.getToPointId());return fromPoint.getID().equals(toPoint.getID());//简化了原函数}}- 对被测类中@Autowired的对象,用@Mocks标注;对被测类自己,用@InjectMocks标注。
1234567891011121314 public class GameHelperTest {@Mockprivate PointRepository pointRepository;@InjectMocksprivate GameHelper gamehelper; //pointRepository作为mock对象被注入到gamehelper中,gamehelper其他成员变量不变public void testCheckLineItem() {Line line = new Line(***);when(pointRepository.findById(123L)).thenReturn(new Point(***));when(pointRepository.findById(456L)).thenReturn(new Point(***));assertTrue(gamehelper.checkLineItem(line));}...}官方解释:
@InjectMocks - injects mock or spy fields into tested object automatically.
- 被
@Mock
标注的对象会自动注入到被@InjectMocks
标注的对象中。- 在上例中, GameHelper 中的成员变量pointRepository(的函数),就会被我们用在测试用例中改写过返回值的pointRepository对象替换掉。
另外,经测试,thenReturn返回的是对象引用而不是深复制了对象本身(所以可以减少写thenReturn()的次数)。
被测函数调用被测类其他函数,怎么控制返回值?
...//省略成员变量 public FullGame createGame(String name, Long creatorId, List<Point> points, List<Selection> selections, List<Line> lines) { Game gameItem = createBlankGame(name, creatorId); //createBlankGame()为CreateGameServiceImpl中另一个函数
那么,如果我还没实现createBlackGame(),我在测试函数里应该怎么控制它呢?这次用2)中的方法@Mock + @InjectMocks就不行了,因为他们属于同一个类。
(这个问题@Xander 觉得应该实现了被调用的函数才好,但是既然mock的存在很多时候是为了在函数都没实现的情况下编写测试,因此我觉得继续研究。)
后来自己通过查阅 官方的文档,解决办法 是使用spy()命令,结合doReturn():
public class CreateGameServiceImplTest {
//这部分不需要改。省略其他成员变量
@Mock
private GameHelper gameHelper;@InjectMocks CreateGameServiceImpl serviceimpl; @Test public void testCreateGameStringLongListOfPointListOfSelectionListOfLine() { serviceimpl = spy(serviceimpl); //将serviceimpl部分mock化 doReturn(***).when(serviceimpl).createBlankGame(a, b); //这里必须用doReturn()而不能是when().thenReturn()
原因我们在最后解释。
首先我们来看文档中对于Spy()的解释:
You can create spies of real objects. When you use the spy then the methods are called (unless a method was stubbed).
Spying on real objects can be associated with “partial mocking” concept.(重点是,spy与”部分mock”相关。)
对于Spy,官方有个Sample:
123456789101112131415 List list = new LinkedList();List spy = spy(list);//optionally, you can stub out some methods:when(spy.size()).thenReturn(100);//using the spy calls real methodsspy.add("one");spy.add("two");//prints "one" - 这个函数还是真实的System.out.println(spy.get(0));//100 is printed - size()函数被替换了System.out.println(spy.size());- 通俗来讲,在我个人理解,Spy()可以使一个对象的一部分方法被用户替换。
- 在我们的例子中,CreateGameServiceImpl中的函数
createGame()
调用了createBlankGame()
,而后者可能是未实现的。- 但是此时CreateGameServiceImpl类的注解是
@InjectMocks
而不是@Mock
,只能接收@Mock
对象的注入,而自己的方法无法被mock(stub)。- 因此我们通过
spy()
,将CreateGameServiceImpl部分mock化,从而将createBlankGame()函数替换掉。- 不过这里如果遇到private的被调函数就没办法了。
在JUnit中集成Spring上下文的支持
- 使用JUnit 4.x提供的注解
@RunWith
,可以指定单元测试的“运行类”,运行类必须继承自org.junit.runner.Runner
并实现 run 方法。- Spring Test 框架提供的运行类是 SpringJUnit4ClassRunner ,使用该类可以轻松的将Spring和JUnit进行集成。该类的用法示例如下:
12345678910111213141516171819202122232425262728293031323334353637383940 @RunWith ( SpringJUnit4ClassRunner.class ) //指定单元测试运行类@SpringBootTest(classes = Application.class)//很多情况下单元测试离不开事务,下面的注解指明使用的事务管理器//如果defaultRollback为true,测试运行结束后,默认回滚事务,不影响数据库@TransactionConfiguration ( transactionManager = "txManager", defaultRollback = true )@Transactional //指定默认所有测试方法的事务特性public class AccountServiceTest{@Injectprivate SpringManagedBean bean; //任何Spring管理的Bean都可以注入到单元测试类@BeforeClasspublic static void setUpBeforeClass() throws Exception{}@AfterClasspublic static void tearDownAfterClass() throws Exception{}@Beforepublic void setUp() throws Exception{}@Afterpublic void tearDown() throws Exception{}@Repeat ( 10 )//重复测试10次//该测试期望抛出IllegalArgumentException,测试超时1秒@Test ( expected = IllegalArgumentException.class, timeout = 1000 )@Rollback ( true )//测试完毕后回滚public void test(){}}Spring MVC的测试
以前的测试方式
12345678910111213141516171819202122 package com.sishuok.mvc.controller;//省略importpublic class UserControllerTest {private UserController userController;@Beforepublic void setUp() {userController = new UserController();//安装userCtroller依赖 比如userService}@Testpublic 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提供的MockAPI模拟一个HttpServletRequest,其他的Servlet API也提供了相应的Mock类,具体请查看Javadoc;
- 访问控制器方法:通过直接调用控制器方法进行访问,此处无法验证SpringMVC框架的类型转换、数据验证等是否正常;
- ModelAndViewAssert:通过这个Assert API验证我们的返回值是否正常;
这种方式的缺点:
- 如不能走Spring MVC完整流程(不能走Servlet的过滤器链、SpringMVC的类型转换、数据验证、数据绑定、拦截器等等),如果做基本的测试没问题,这种方式就是纯粹的单元测试,我们想要的功能其实是一种集成测试,不过后续部分不区分
安装测试环境
- spring mvc测试框架提供了两种方式,独立安装和集成Web环境测试(此种方式并不会集成真正的web环境,而是通过相应的Mock API进行模拟测试,无须启动服务器)。
独立测试方式
1234567 public class UserControllerStandaloneSetupTest {private MockMvc mockMvc;@Beforepublic void setUp() {UserController userController = new UserController();mockMvc = MockMvcBuilders.standaloneSetup(userController).build();}- 首先自己创建相应的控制器,注入相应的依赖
- 通过MockMvcBuilders.standaloneSetup模拟一个Mvc测试环境,通过build得到一个MockMvc
- MockMvc:是我们以后测试时经常使用的API,后边介绍
测试API
Spring mvc测试框架提供了测试MVC需要的API,主要包括
- Servlet/JSPMock
- MockMvcBuilder
- MockMvc
- RequestBuilder
- ResultMatcher
- ResultHandler
- MvcResult 等
另外提供了几个静态工厂方法便于测试:
- MockMvcBuilders
- MockMvcRequestBuilder
- MockMvcResultMatchers
- MockMvcResultHandler
在使用时请使用静态方法导入方便测试,如:
1234 import static org.springframework.test.web.servlet.setup.MockMvcBuilders.*;import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;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包。
API如下:
MockMvcBuilder/MockMvcBuilders主要API:
- MockMvcBuilder是用来构造MockMvc的构造器,其主要有两个实现:
StandaloneMockMvcBuilder
DefaultMockMvcBuilder
- 分别对应之前的两种测试方式。对于我们来说直接使用静态工厂MockMvcBuilders创建即可:
MockMvcBuilders.webAppContextSetup(WebApplicationContextcontext)
指定WebApplicationContext,将会从该上下文获取相应的控制器并得到相应的MockMvc;MockMvcBuilders.standaloneSetup(Object... controllers)
通过参数指定一组控制器,这样就不需要从上下文获取了DefaultMockMvcBuilder主要API:
StandaloneMockMvcBuilder继承了DefaultMockMvcBuilder
API说明 addFilters(Filter... filters)/addFilter(Filter filter, String... urlPatterns)
添加javax.servlet.Filter过滤器 defaultRequest(RequestBuilderrequestBuilder)
默认的RequestBuilder,每次执行时会合并到自定义的RequestBuilder中,即提供公共请求数据的; alwaysExpect(ResultMatcher resultMatcher)
定义全局的结果验证器,即每次执行请求时都进行验证的规则; alwaysDo(ResultHandler resultHandler)
定义全局结果处理器,即每次请求时都进行结果处理; dispatchOptions
DispatcherServlet是否分发OPTIONS请求方法到控制器; MockMvc主要API:
API说明 addMappedInterceptors(String[]pathPatterns,HandlerInterceptor… interceptors) 添加spring mvc拦截器; setContentNegotiationManager(ContentNegotiationManager contentNegotiationManager) 设置内容协商管理器; setAsyncRequestTimeout(long timeout) 设置异步超时时间; setCustomArgumentResolvers(HandlerMethodArgumentResolver… argumentResolvers) 设置自定义控制器方法参数解析器; setCustomReturnValueHandlers(HandlerMethodReturnValueHandler… handlers) 设置自定义控制器方法返回值处理器; setHandlerExceptionResolvers(List 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中的占位符替代; API说明 setDefaultRequest 设置默认的RequestBuilder,用于在每次perform执行相应的RequestBuilder时自动把该默认的RequestBuilder合并到perform的RequestBuilder中; setGlobalResultMatchers 设置全局的预期结果验证规则,如我们通过MockMvc测试多个控制器时,假设它们都想验证某个规则时,就可以使用这个; setGlobalResultHandlers 设置全局的ResultHandler结果处理器;
12345 MvcResult result = mockMvc.perform(MockMvcRequestBuilders.get("/user/1")).andExpect(MockMvcResultMatchers.view().name("user/view")).andExpect(MockMvcResultMatchers.model().attributeExists("user")).andDo(MockMvcResultHandlers.print()).andReturn();RequestBuilder
/
MockMvcRequestBuilders
RequestBuilder用来构建请求的,其提供了一个方法buildRequest(ServletContextservletContext)用于构建MockHttpServletRequest 其主要有两个子类。
- MockHttpServletRequestBuilder
- MockMultipartHttpServletRequestBuilder(如文件上传使用)
用来Mock客户端请求需要的所有数据。
MockMvcRequestBuilders主要API:
MockHttpServletRequestBuilder和MockMultipartHttpServletRequestBuilder API:
MockHttpServletRequestBuilder API:
API说明 MockHttpServletRequestBuilderget(StringurlTemplate,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方法; MockHttpServletRequestBuilderrequest(HttpMethodhttpMethod,StringurlTemplate,Object…urlVariables) 提供自己的Http请求方法及uri模板和uri变量,如上API都是委托给这个API; MockMultipartHttpServletRequestBuilderfileUpload(StringurlTemplate,Object…urlVariables) 提供文件上传方式的请求,得到MockMultipartHttpServletRequestBuilder; RequestBuilder asyncDispatch(finalMvcResultmvcResult) 创建一个从启动异步处理的请求的MvcResult进行异步分派的RequestBuilder; MockMultipartHttpServletRequestBuilder继承自MockHttpServletRequestBuilder,又提供了如下API:
API说明 MockHttpServletRequestBuilderaccept(MediaType…mediaTypes)/MockHttpServletRequestBuilderaccept(String…mediaTypes) 指定请求的Accept头信息 MockHttpServletRequestBuildercontent(byte[]content)/MockHttpServletRequestBuildercontent(Stringcontent) 指定请求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) 置请求session属性数据; MockHttpServletRequestBuilder sessionAttrs(Map sessionAttributes) 置请求session属性数据 MockHttpServletRequestBuilder flashAttr(String name, Object value) 指定请求的flash信息,比如重定向后的属性信息 MockHttpServletRequestBuilder flashAttrs(Map flashAttributes) 指定请求的flash信息,比如重定向后的属性信息 MockHttpServletRequestBuilder session(MockHttpSession session) 指定请求的Session MockHttpServletRequestBuilder principal(Principal principal) 指定请求的Principal MockHttpServletRequestBuildercontextPath(StringcontextPath) 指定请求的上下文路径,必须以“/”开头,且不能以“/”结尾 MockHttpServletRequestBuilder pathInfo(String pathInfo) 请求的路径信息,必须以“/”开头 MockHttpServletRequestBuilder secure(boolean secure) 请求是否使用安全通道 MockHttpServletRequestBuilderwith(RequestPostProcessorpostProcessor) 请求的后处理器,用于自定义一些请求处理的扩展点 ResultActions
- 调用
MockMvc.perform(RequestBuilder requestBuilder)
后将得到ResultActions,通过ResultActions完成如下三件事:- 得到相应的ResultMatchers后,接着再调用其相应的API得到ResultMatcher,
- 如
ModelResultMatchers.attributeExists(final String...names)
判断Model属性是否存在。具体请查看相应的API。ResultHandler/MockMvcResultHandlers
- ResultHandler用于对处理的结果进行相应处理的,比如输出整个请求/响应等信息方便调试
- Springmvc测试框架提供了MockMvcResultHandlers静态工厂方法,该工厂提供了
ResultHandlerprint()
返回一个输出MvcResult详细信息到控制台的ResultHandler实现。MvcResult
API说明 ResultMatcher forwardedUrlPattern(final String urlPattern) 验证处理完请求后转发的url(Ant风格模式匹配,@since spring4) ResultMatcher redirectedUrl(final String expectedUrl) 验证处理完请求后重定向的url(绝对匹配) ResultMatcher redirectedUrlPattern(final StringexpectedUrl) 验证处理完请求后重定向的url(Ant风格模式匹配,@since spring4)
1234567 //测试普通控制器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到控制台测试普通控制器,但是URL错误,即404
123456 //找不到控制器,404测试MvcResult result = mockMvc.perform(get("/user2/{id}", 1)) //执行请求.andDo(print()).andExpect(status().isNotFound()) //验证控制器不存在.andReturn();Assert.assertNull(result.getModelAndView()); //自定义断言得到MvcResult自定义验证
123 MvcResult result = mockMvc.perform(get("/user/{id}", 1))//执行请求.andReturn(); //返回MvcResultAssert.assertNotNull(result.getModelAndView().getModel().get("user")); //自定义断言验证请求参数绑定到模型数据及Flash属性
123456 mockMvc.perform(post("/user").param("name", "zhang")) //执行传递参数的POST请求(也可以post("/user?name=zhang")).andExpect(handler().handlerType(UserController.class)) //验证执行的控制器类型.andExpect(handler().methodName("create")) //验证执行的控制器方法名.andExpect(model().hasNoErrors()) //验证页面没有错误.andExpect(flash().attributeExists("success")) //验证存在flash属性.andExpect(view().name("redirect:/user")); //验证视图验证请求参数验证失败出错
1234 mockMvc.perform(post("/user").param("name", "admin")) //执行请求.andExpect(model().hasErrors()) //验证模型有错误.andExpect(model().attributeDoesNotExist("name")) //验证存在错误的属性.andExpect(view().name("showCreateForm")); //验证视图
12345 //文件上传byte[] bytes = new byte[] {1, 2};mockMvc.perform(fileUpload("/user/{id}/icon", 1L).file("icon", bytes)) //执行文件上传.andExpect(model().attribute("icon", bytes)) //验证属性相等性.andExpect(view().name("success")); //验证视图JSON请求/响应验证
- 测试时需要安装jackson Json和JsonPath依赖:
123456789101112131415 String requestBody = "{\"id\":1, \"name\":\"zhang\"}";mockMvc.perform(post("/user").contentType(MediaType.APPLICATION_JSON).content(requestBody).accept(MediaType.APPLICATION_JSON)) //执行请求.andExpect(content().contentType(MediaType.APPLICATION_JSON)) //验证响应contentType.andExpect(jsonPath("$.id").value(1)); //使用Json path验证JSON 请参考http://goessner.net/articles/JsonPath/String errorBody = "{id:1, name:zhang}";MvcResult result = mockMvc.perform(post("/user").contentType(MediaType.APPLICATION_JSON).content(errorBody).accept(MediaType.APPLICATION_JSON)) //执行请求.andExpect(status().isBadRequest()) //400错误请求.andReturn();Assert.assertTrue(HttpMessageNotReadableException.class.isAssignableFrom(result.getResolvedException().getClass()));//错误的请求内容体XML请求/响应验证
- 测试时需要安装
spring oxm
和xstream
依赖:
1234567891011121314151617 //XML请求/响应String requestBody = "<user><id>1</id><name>zhang</name></user>";mockMvc.perform(post("/user").contentType(MediaType.APPLICATION_XML).content(requestBody).accept(MediaType.APPLICATION_XML)) //执行请求.andDo(print()).andExpect(content().contentType(MediaType.APPLICATION_XML)) //验证响应contentType.andExpect(xpath("/user/id/text()").string("1")); //使用XPath表达式验证XML 请参考http://www.w3school.com.cn/xpath/String errorBody = "<user><id>1</id><name>zhang</name>";MvcResult result = mockMvc.perform(post("/user").contentType(MediaType.APPLICATION_XML).content(errorBody).accept(MediaType.APPLICATION_XML)) //执行请求.andExpect(status().isBadRequest()) //400错误请求.andReturn();Assert.assertTrue(HttpMessageNotReadableException.class.isAssignableFrom(result.getResolvedException().getClass()));//错误的请求内容体异常处理
123456 //异常处理MvcResult result = mockMvc.perform(get("/user/exception")) //执行请求.andExpect(status().isInternalServerError()) //验证服务器内部错误.andReturn();Assert.assertTrue(IllegalArgumentException.class.isAssignableFrom(result.getResolvedException().getClass()));静态资源
1234567 //静态资源mockMvc.perform(get("/static/app.js")) //执行请求.andExpect(status().isOk()) //验证状态码200.andExpect(content().string(CoreMatchers.containsString("var")));//验证渲染后的视图内容包含varmockMvc.perform(get("/static/app1.js")) //执行请求.andExpect(status().isNotFound()); //验证状态码404异步测试
12345678910 //CallableMvcResult result = mockMvc.perform(get("/user/async1?id=1&name=zhang")) //执行请求.andExpect(request().asyncStarted()).andExpect(request().asyncResult(CoreMatchers.instanceOf(User.class))) //默认会等10秒超时.andReturn();mockMvc.perform(asyncDispatch(result)).andExpect(status().isOk()).andExpect(content().contentType(MediaType.APPLICATION_JSON)).andExpect(jsonPath("$.id").value(1));
12345678910 //DeferredResultresult = mockMvc.perform(get("/user/async2?id=1&name=zhang")) //执行请求.andExpect(request().asyncStarted()).andExpect(request().asyncResult(CoreMatchers.instanceOf(User.class))) //默认会等10秒超时.andReturn();mockMvc.perform(asyncDispatch(result)).andExpect(status().isOk()).andExpect(content().contentType(MediaType.APPLICATION_JSON)).andExpect(jsonPath("$.id").value(1));- 此处请在第一次请求时加上 andExpect(request().asyncResult(CoreMatchers.instanceOf(User.class))) 这样会等待结果返回/超时,无须自己设置线程等待了;此处注意request().asyncResult一定是在第一次请求发出;然后第二次通过asyncDispatch进行异步请求。
添加自定义过滤器
12 mockMvc = webAppContextSetup(wac).addFilter(new MyFilter(), "/*").build();mockMvc.perform(get("/user/1")).andExpect(request().attribute("filter", true));全局配置
12345678 mockMvc = webAppContextSetup(wac).defaultRequest(get("/user/1").requestAttr("default", true)) //默认请求 如果其是Mergeable类型的,会自动合并的哦mockMvc.perform中的RequestBuilder.alwaysDo(print()) //默认每次执行请求后都做的动作.alwaysExpect(request().attribute("default", true)) //默认每次执行后进行验证的断言.build();mockMvc.perform(get("/user/1")).andExpect(model().attributeExists("user"));记住测试步骤,按照步骤操作,整个测试过程是非常容易理解的:
- 准备测试环境
- 通过MockMvc执行请求
- 添加验证断言
- 添加结果处理器
- 得到MvcResult进行自定义断言/进行下一步的异步请求
- 卸载测试环境
集成Web环境方式
1234567891011121314151617181920212223242526 //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 {@Autowiredprivate WebApplicationContext wac;private MockMvc mockMvc;@Beforepublic void setUp() {mockMvc = MockMvcBuilders.webAppContextSetup(wac).build();}}- 通过
@Autowired
WebApplicationContextwac注入web环境的ApplicationContext容器;- 然后通过
MockMvcBuilders.webAppContextSetup(wac).build()
创建一个MockMvc进行测试;开始测试
12345678910 @Testpublic 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"));}- mockMvc.perform 执行一个请求;
- MockMvcRequestBuilders.get(“/user/1”) 构造一个请求
- ResultActions.andExpect 添加执行完成后的断言
- ResultActions.andDo 添加一个结果处理器,表示要对结果做点什么事情,比如此处使用MockMvcResultHandlers.print() 输出整个响应结果信息。
- ResultActions.andReturn 表示执行完成后返回相应的结果。
Contents
- 1. 基于Spring web 做单元测试
- 2. Mock
- 3. Spring与单元测试
- 4. 被测类中@Autowired注解,如何控制其中Repository返回值
- 5. 在JUnit中集成Spring上下文的支持
- 6. Spring MVC的测试
- 7. 安装测试环境
- 8. 测试示例
- 9. 记住测试步骤,按照步骤操作,整个测试过程是非常容易理解的: