Junit测试Controller(MockMVC使⽤),传输@RequestBody数
据解决办法
⼀、单元测试的⽬的
  简单来说就是在我们增加或者改动⼀些代码以后对所有逻辑的⼀个检测,尤其是在我们后期修改后(不论是增加新功能,修改bug),都可以做到重新测试的⼯作。以减少我们在发布的时候出现更过甚⾄是出现之前解决了的问题再次重现。
  这⾥主要是使⽤MockMvc对我们的系统的Controller进⾏单元测试。
  对数据库的操作使⽤事务实现回滚,及对数据库的增删改⽅法结束后将会还远数据库。
⼆、MockMvc的使⽤
1、⾸先我们上⼀个例⼦,
import org.apachemons.logging.Log;
import org.apachemons.logging.LogFactory;
springframework事务
import org.junit.Before;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import st.context.ContextConfiguration;
import st.context.junit4.SpringJUnit4ClassRunner;
import ansaction.TransactionConfiguration;
import st.context.web.WebAppConfiguration;
import st.web.servlet.MockMvc;
import st.web.servlet.RequestBuilder;
import st.web.servlet.ResultActions;
import st.web.servlet.setup.MockMvcBuilders;
import ansaction.annotation.Transactional;
import org.t.WebApplicationContext;
import static st.;
import static st.quest.MockMvcRequestBuilders.post;
import static st.sult.MockMvcResultHandlers.print;
import static st.sult.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= Log(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层。
@webappconfiguration是⼀级注释,⽤于声明⼀个ApplicationContext集成测试加载WebApplicationContext。作⽤是模拟ServletContext
@ContextConfiguration:因为controller,component等都是使⽤注解,需要注解指定spring的配置⽂件,扫描相应的配置,将类初始化等。@TransactionConfiguration(transactionManager = "transactionManager", defaultRollback = true)
@Transactional
上⾯两句的作⽤是,让我们对数据库的操作会事务回滚,如对数据库的添加操作,在⽅法结束之后,会撤销我们对数据库的操作。
为什么要事务回滚?
测试过程对数据库的操作,会产⽣脏数据,影响我们数据的正确性
不⽅便循环测试,即假如这次我们将⼀个记录删除了,下次就⽆法再进⾏这个Junit测试了,因为该记录已经删除,将会报错。
如果不使⽤事务回滚,我们需要在代码中显式的对我们的增删改数据库操作进⾏恢复,将多很多和测试⽆关的代码
⽅法解析:
perform:执⾏⼀个RequestBuilder请求,会⾃动执⾏SpringMVC的流程并映射到相应的控制器执⾏处理;
get:声明发送⼀个get请求的⽅法。MockHttpServletRequestBuilder get(String urlTemplate, urlVariables):根据uri模板和uri变量值得到⼀个GET请求⽅式的。另外提供了其他的请求的⽅法,如:post、put、delete等。
param:添加request的参数,如上⾯发送请求的时候带上了了pcode = root的参数。假如使⽤需要发送json数据格式的时将不能使⽤这种⽅式,可见后⾯被@ResponseBody注解参数的解决⽅法
andExpect:添加ResultMatcher验证规则,验证控制器执⾏完成后结果是否正确(对返回的数据进⾏的判断);
andDo:添加ResultHandler结果处理器,⽐如调试时打印结果到控制台(对返回的数据进⾏的判断);
andReturn:最后返回相应的MvcResult;然后进⾏⾃定义验证/进⾏下⼀步的异步处理(对返回的数据进⾏的判断);
注意事项:
在mac上使⽤log4j是,假如使⽤了${catalina.home}需要注意,mac不会去到tomcat所在的路径,直接回到根路径 “/”,⽽正常情况下,根路径是没有写权限的,需要使⽤管理员赋权限。
log4j在配置完成之后,需要设置起打印⽇志的级别,假如没有设置,在Junit中,将⽆法打印⽇志。
3、后台的返回数据中,最好带上我们对数据库的修改的结果返回的前端。
为什么要在data中返回⼀个修改或者添加的对象
将数据返回给前端,前端容易判断数据是否添加或者修改成功
更新或者添加完数据经常需要刷新页⾯,将数据直接给了前端,前端不⽤再发⼀个请求来获取
单元测试的时候,能对数据库的DDL(增删改)操作的时候,我们能对数据进⾏审核,从何判断我们的操作是否是成功的。如下⾯的例⼦:
我们发送⼀个添加操作,添加⼀个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=“测试”。
@Test
public void testCreateSeewoAccountUser() throws Exception {
mockMvc.perform(post("/users")
.contentType(MediaType.APPLICATION_FORM_URLENCODED)
).andExpect(status().isOk())
.andExpect(jsonPath("$.data.name", is("测试"))))
.andExpect(jsonPath("$.ateTime", notNullValue()))
;
}
三、遇到的问题
1、发送⼀个被@ResponseBody标识的参数,⼀直到400错误。即⽆法发送⼀个json格式的数据到Controller层。
      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 = JSONString(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依赖
    <dependency>
<groupId>com.jayway.jsonpath</groupId>
<artifactId>json-path</artifactId>
<version>0.8.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.jayway.jsonpath</groupId>
<artifactId>json-path-assert</artifactId>
<version>0.8.1</version>
<scope>test</scope>
</dependency>
学习链接:

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。