一、单元测试的目的
简单来说就是在我们增加或者改动一些代码以后对所有逻辑的一个检测,尤其是在我们后期修改后(不论是增加新功能,修改bug),都可以做到重新测试的工作。以减少我们在发布的时候出现更过甚至是出现之前解决了的问题再次重现。
这里主要是使用MockMvc对我们的系统的Controller进行单元测试。
对数据库的操作使用事务实现回滚,及对数据库的增删改方法结束后将会还远数据库。
二、MockMvc的使用
1、首先我们上一个例子,
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.junit.Before;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.transaction.TransactionConfiguration;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.RequestBuilder;
import org.springframework.test.web.servlet.ResultActions;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.context.WebApplicationContext;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
/**
* Created by zhengcanrui on 16/8/11.
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations={"classpath:spring/applicationContext-*xml"})
//配置事务的回滚,对数据库的增删改都会回滚,便于测试用例的循环利用
@TransactionConfiguration(transactionManager = "transactionManager", defaultRollback = true)
@Transactional
@WebAppConfiguration
public class Test {
//记得配置log4j.properties ,的命令行输出水平是debug
protected Log logger= LogFactory.getLog(TestBase.class);
protected MockMvc mockMvc;
@Autowired
protected WebApplicationContext wac;
@Before() //这个方法在每个方法执行之前都会执行一遍
public void setup() {
mockMvc = MockMvcBuilders.webAppContextSetup(wac).build(); //初始化MockMvc对象
}
@org.junit.Test
public void getAllCategoryTest() throws Exception {
String responseString = mockMvc.perform(
get("/categories/getAllCategory") //请求的url,请求的方法是get
.contentType(MediaType.APPLICATION_FORM_URLENCODED) //数据的格式
.param("pcode","root") //添加参数
).andExpect(status().isOk()) //返回的状态是200
.andDo(print()) //打印出请求和相应的内容
.andReturn().getResponse().getContentAsString(); //将相应的数据转换为字符串
System.out.println("--------返回的json = " + responseString);
}
}
Spring MVC的测试往往看似比较复杂。其实他的不同在于,他需要一个ServletContext来模拟我们的请求和响应。但是Spring也针对Spring MVC 提供了请求和响应的模拟测试接口,以方便我们的单元测试覆盖面不只是service,dao层。
2、代码解释:
@webappconfiguration是一级注释,用于声明一个ApplicationContext集成测试加载WebApplicationContext。作用是模拟ServletContext
@ContextConfiguration:因为controller,component等都是使用注解,需要注解指定spring的配置文件,扫描相应的配置,将类初始化等。
@TransactionConfiguration(transactionManager = "transactionManager", defaultRollback = true) @Transactional
上面两句的作用是,让我们对数据库的操作会事务回滚,如对数据库的添加操作,在方法结束之后,会撤销我们对数据库的操作。
为什么要事务回滚?
方法解析:
注意事项:
3、后台的返回数据中,最好带上我们对数据库的修改的结果返回的前端。
为什么要在data中返回一个修改或者添加的对象
我们发送一个添加操作,添加一个SoftInfo对象,SoftInfo类定义如下:
public class SoftInfo { private String id; private String name; }
添加完之后,由于我们进行了单元测试的事务回滚,我们将不能再数据库中看我们我们的的添加操作,无法判断操作是否成功。
为了解决上面的问题,我们可以在返回的json的数据中添加一个“data”字段,解析该json中的data字段数据,判断我们的添加操作是否成功的。json格式如下:
{ "status":200, "data":{"id":"2","name":"测试"} }
我们可以使用andExpect方法对返回的数据进行判断,用“$.属性”获取里面的数据,如我要获取返回数据中的"data.name",可以写成"$.data.name"。下面的例子是判断返回的data.name=“测试”。
三、遇到的问题
1、发送一个被@ResponseBody标识的参数,一直到400错误。 即无法发送一个json格式的数据到Controller层。
解决方法1:
SoftInfo softInfo = new SoftInfo(); //设置值 ObjectMapper mapper = new ObjectMapper(); ObjectWriter ow = mapper.writer().withDefaultPrettyPrinter(); java.lang.String requestJson = ow.writeValueAsString(softInfo); String responseString = mockMvc.perform( post("/softs").contentType(MediaType.APPLICATION_JSON).content(requestJson)).andDo(print()) .andExpect(status().isOk()).andReturn().getResponse().getContentAsString();
解决方法2:使用com.alibaba.fastjson.JSONObject将对象转换为Json数据
SoftInfo softInfo = new SoftInfo(); //。。。设置值 String requestJson = JSONObject.toJSONString(folderInfo); String responseString = mockMvc.perform( post("/softs").contentType(MediaType.APPLICATION_JSON).content(requestJson)).andDo(print()) .andExpect(status().isOk()).andReturn().getResponse().getContentAsString();
注意上面contentType需要设置成MediaType.APPLICATION_JSON,即声明是发送“application/json”格式的数据。使用content方法,将转换的json数据放到request的body中。
2、java.lang.NoClassDefFoundError: com/jayway/jsonpath/InvalidPathException
缺少了jar包:
可以添加一下的maven依赖
com.jayway.jsonpath json-path 0.8.1 test com.jayway.jsonpath json-path-assert 0.8.1 test