上篇文章中我们介绍了如何在struts环境下,进行模拟action的请求测试,以及我们使用了EasyMock框架,来模拟对象的行为。这篇文章我们会继续介绍spring mvc环境下如何对controller进行单元测试。另外我们带来一种全新的mock框架mockito。
一、准备工作,引入以下maven坐标
当然别忘记了junit,我们所使用的spring-test框架都是基于junit
二、整体文件
所有测试文件,均要求放入test目录下(一种规范)
MyControllerTest.java测试主类
import com.jd.plugin.web.springmvc.service.TestService;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
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.servlet.ModelAndView;
import static org.hamcrest.core.Is.is;
import static org.mockito.Mockito.when;
import static org.mockito.Mockito.verify;
import staticorg.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import staticorg.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import staticorg.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
/**
* Created by wangxindong on 2016/11/24.
*/
@RunWith(SpringJUnit4ClassRunner.class)
//@WebAppConfiguration
@ContextConfiguration(locations={"classpath*:/spring-config-dispatcher.xml"})
public class MyControllerTest {
private MockMvc mockMvc;
@Mock//如果该对象需要mock,则加上此Annotate,在这里我们就是要模拟testService的query()行为
private TestService testService;
@InjectMocks//使mock对象的使用类可以注入mock对象,在这里myController使用了testService(mock对象),所以在MyController此加上此Annotate
MyController myController;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
this.mockMvc = MockMvcBuilders.standaloneSetup(myController).build();//这个对象是Controller单元测试的关键
}
@Test
public voidtestQueryDataFromController() throws Exception{
//静态引入使得很像人类的语言,当...然后返回...
when(testService.query("code-1001","name-wangxindong")).thenReturn("成功");//这里模拟行为,返回"成功" 而不是原始的"test-code-name"
mockMvc.perform(get("/mytest/query/queryData")
.param("code","code-1001")
.param("name","name-wangxindong"))
.andDo(print());
// .andExpect(status().isOk()).andExpect(content().string(is("{\"status\":\""+ result + "\"}")));
verify(testService).query("code-1001","name-wangxindong");
}
}
MyController.java 被测试的controller
import com.jd.plugin.web.springmvc.service.TestService;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
/**
* Created by wangxindong on 2016/11/24.
*/
@RequestMapping("/mytest")
@Controller
public class MyController {
@Resource
private TestService testService;
@RequestMapping(value ="/query/queryData", method = RequestMethod.GET)
@ResponseBody
public StringqueryData(@RequestParam(required = true, value = "code", defaultValue= "") String code,
@RequestParam(value = "name", defaultValue = "")String name,
HttpServletRequest request){
System.out.println("code:"+code);
System.out.println("name:"+name);
String result =testService.query(code,name);
return "result";
}
}
TestService.java 业务类
/**
* Created by wangxindong on 2016/11/24.
*/
public interface TestService {
public String query(Stringcode,String name);
}
TestServiceImpl.java业务实现类
import com.jd.plugin.web.springmvc.service.TestService;
/**
* Created by wangxindong on 2016/11/24.
*/
public class TestServiceImpl implements TestService {
public String query(String code,String name) {
System.out.println("TestServiceImplcode:"+code);
System.out.println("TestServiceImplname:"+name);
return "test-code-name";
}
}
三、文件逐步说明
1、Spring Test 组件使用
@RunWith(SpringJUnit4ClassRunner.class) 表示使用SpringTest组件进行单元测试,这个大家应该都很熟悉了,我们一直是这样使用的
@ContextConfiguration(locations={"classpath*:/spring-config-dispatcher.xml"})指定bean的配置文件,加载上下文环境,我们前面的文章都是这样使用的。其实还有一种文件路径的方式,比如:@ContextConfiguration("file:src/main/webapp/WEB-INF/mvc-dispatcher-servlet.xml"),一般个人喜欢第一种方式。
2、mockito使用说明
@Mock
private TestService testService;
如果该对象需要mock,则加上此Annotate,在这里我们就是要模拟testService的query()行为
@InjectMocks
MyController myController;
使mock对象的使用类可以注入mock对象,在这里myController使用了testService(mock对象),所以在MyController此加上此Annotate
this.mockMvc = MockMvcBuilders.standaloneSetup(myController).build();//这个对象是Controller单元测试的关键
还有几点重要说明:
四、运行结果,这些都是print()打印出的内容
MockHttpServletRequest:
HTTP Method = GET
Request URI = /mytest/query/queryData
Parameters = {code=[code-1001],name=[name-wangxindong]}
Headers = {}
Handler:
Type =com.jd.plugin.web.springmvc.controller.MyController
Method = public java.lang.Stringcom.jd.plugin.web.springmvc.controller.MyController.queryData(java.lang.String,java.lang.String,javax.servlet.http.HttpServletRequest)
Resolved Exception:
Type = null
ModelAndView:
View name = null
View = null
Model = null
FlashMap:
MockHttpServletResponse:
Status = 200
Error message = null
Headers ={Content-Type=[text/plain;charset=ISO-8859-1], Content-Length=[6]}
Content type =text/plain;charset=ISO-8859-1
Body = result
Forwarded URL = null
Redirected URL = null
Cookies= []
总结,spring mvc的web请求,我们一样可以通过Spring Test组件来测试。另外mockito这种mock框架来模拟对象行为的时候更加方便,比早先的easymock使用更加简便。