Junit测试Controller(MockMVC使用),传输@RequestBody数据解决办法

1、单元测试的目的前端

  简单来讲就是在咱们增长或者改动一些代码之后对全部逻辑的一个检测,尤为是在咱们后期修改后(不管是增长新功能,修改bug),均可以作到从新测试的工做。以减小咱们在发布的时候出现更过甚至是出现以前解决了的问题再次重现。java

  这里主要是使用MockMvc对咱们的系统的Controller进行单元测试。web

  对数据库的操做使用事务实现回滚,及对数据库的增删改方法结束后将会还远数据库。spring

2、MockMvc的使用数据库

一、首先咱们上一个例子,apache

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层。json

二、代码解释:spring-mvc

@webappconfiguration是一级注释,用于声明一个ApplicationContext集成测试加载WebApplicationContext。做用是模拟ServletContexttomcat

@ContextConfiguration:由于controller,component等都是使用注解,须要注解指定spring的配置文件,扫描相应的配置,将类初始化等。mvc

@TransactionConfiguration(transactionManager = "transactionManager", defaultRollback = true)
@Transactional

上面两句的做用是,让咱们对数据库的操做会事务回滚,如对数据库的添加操做,在方法结束以后,会撤销咱们对数据库的操做。

为何要事务回滚?

  • 测试过程对数据库的操做,会产生脏数据,影响咱们数据的正确性
  • 不方便循环测试,即假如此次咱们将一个记录删除了,下次就没法再进行这个Junit测试了,由于该记录已经删除,将会报错。
  • 若是不使用事务回滚,咱们须要在代码中显式的对咱们的增删改数据库操做进行恢复,将多不少和测试无关的代码 

方法解析:

  • perform执行一个RequestBuilder请求,会自动执行SpringMVC的流程并映射到相应的控制器执行处理;
  • get:声明发送一个get请求的方法。MockHttpServletRequestBuilder get(String urlTemplate, Object... 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中,将没法打印日志。 

三、后台的返回数据中,最好带上咱们对数据库的修改的结果返回的前端。

为何要在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("$.data.createTime", notNullValue()))
        ;
    }

3、遇到的问题

一、发送一个被@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中。

二、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>

学习连接:https://www.petrikainulainen.net/spring-mvc-test-tutorial/ 

  致谢:感谢您的阅读!

相关文章
相关标签/搜索