SpringBoot教程&笔记|Demo02-添加单元测试

  heardfate

本教程在Demo01基础上添加测试方法

一、单元测试的目的

  简单来说就是在我们增加或者改动一些代码以后对所有逻辑的一个检测,尤其是在我们后期修改后(不论是增加新功能,修改bug),都可以做到重新测试的工作。以减少我们在发布的时候出现更过甚至是出现之前解决了的问题再次重现。
  这里主要是使用MockMvc对我们的系统的Controller进行单元测试。
  对数据库的操作使用事务实现回滚,及对数据库的增删改方法结束后将会还原数据库。

二、添加单元测试

您需要为您添加的端点添加测试,Spring Test已经为此提供了一些机制,并且很容易将其包括在项目中。

添加依赖

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-test</artifactId>
  <scope>test</scope>
</dependency>

编写测试

现在编写一个简单的单元测试,通过端点模拟servlet请求和响应:
src/test/java/com/heardfate/springboot/demo/controller/HelloControllerTest.java

package com.heardfate.springboot.demo.controller;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.MediaType;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;

import static org.hamcrest.Matchers.equalTo;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

/**
 * @since: 2018/10/20
 * @author: Mr.HeardFate
 */
@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureMockMvc
public class HelloControllerTest {

  @Autowired
  private MockMvc mvc;

  @Test
  public void getHello() throws Exception {
  mvc.perform(MockMvcRequestBuilders.get("/")//请求的url,请求的方法是get
	.accept(MediaType.APPLICATION_JSON))//指定请求的Accept头信息
	.andDo(print())//打印出请求和相应的内容
	.andExpect(status().isOk())//返回的状态是200
	.andExpect(content().string(equalTo("Greetings from Spring Boot!")));
  }
}

单元测试

三、执行单元测试

HelloControllerTest.java右击Run即可

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::            (v2.1.0.RC1)

2018-10-20 08:55:17.691  INFO 1223 --- [           main] c.h.s.d.controller.HelloControllerTest   : Starting HelloControllerTest on heardfatedeMac-Pro.local with PID 1223 (started by **** in /****/IdeaProjects/springboot-demo/demo01)
2018-10-20 08:55:17.694  INFO 1223 --- [           main] c.h.s.d.controller.HelloControllerTest   : No active profile set, falling back to default profiles: default
2018-10-20 08:55:19.283  INFO 1223 --- [           main] o.s.s.concurrent.ThreadPoolTaskExecutor  : Initializing ExecutorService 'applicationTaskExecutor'
2018-10-20 08:55:20.491  INFO 1223 --- [           main] o.s.b.t.m.w.SpringBootMockServletContext : Initializing Spring TestDispatcherServlet ''
2018-10-20 08:55:20.491  INFO 1223 --- [           main] o.s.t.web.servlet.TestDispatcherServlet  : Initializing Servlet ''
2018-10-20 08:55:20.514  INFO 1223 --- [           main] o.s.t.web.servlet.TestDispatcherServlet  : Completed initialization in 23 ms
2018-10-20 08:55:20.578  INFO 1223 --- [           main] c.h.s.d.controller.HelloControllerTest   : Started HelloControllerTest in 8.326 seconds (JVM running for 9.627)

MockHttpServletRequest:
      HTTP Method = GET
      Request URI = /
       Parameters = {}
          Headers = {Accept=[application/json]}
             Body = null
    Session Attrs = {}

Handler:
             Type = com.heardfate.springboot.demo.controller.HelloController
           Method = public java.lang.String com.heardfate.springboot.demo.controller.HelloController.index()

Async:
    Async started = false
     Async result = null

Resolved Exception:
             Type = null

ModelAndView:
        View name = null
             View = null
            Model = null

FlashMap:
       Attributes = null

MockHttpServletResponse:
           Status = 200
    Error message = null
          Headers = {Content-Type=[application/json;charset=UTF-8], Content-Length=[27]}
     Content type = application/json;charset=UTF-8
             Body = Greetings from Spring Boot!
    Forwarded URL = null
   Redirected URL = null
          Cookies = []
2018-10-20 08:55:21.067  INFO 1223 --- [       Thread-2] o.s.s.concurrent.ThreadPoolTaskExecutor  : Shutting down ExecutorService 'applicationTaskExecutor'

Process finished with exit code 0

单元测试

四、补充说明

  • @RunWith(SpringRunner.class): 表示使用Spring Test组件进行单元测试;
  • @SpringBootTest: 使用@SpringBootTest加载测试的spring上下文环境;
  • @AutoConfigureMockMvc: 自动配置MockMvc这个类

RequestBuilder/MockMvcRequestBuilders

从名字可以看出,RequestBuilder用来构建请求的,其提供了一个方法buildRequest(ServletContext servletContext)用于构建MockHttpServletRequest;其主要有两个子类MockHttpServletRequestBuilder和MockMultipartHttpServletRequestBuilder(如文件上传使用),即用来Mock客户端请求需要的所有数据。

MockMvcRequestBuilders主要API


MockHttpServletRequestBuilder get(String urlTemplate, Object… urlVariables):根据uri模板和uri变量值得到一个GET请求方式的MockHttpServletRequestBuilder;如get(/user/{id}, 1L);

MockHttpServletRequestBuilder post(String urlTemplate, Object… urlVariables):同get类似,但是是POST方法;提供自己的Http请求方法及uri模板和uri变量,如上API都是委托给这个API;

MockMultipartHttpServletRequestBuilder fileUpload(String urlTemplate, Object… urlVariables):提供文件上传方式的请求,得到MockMultipartHttpServletRequestBuilder;

RequestBuilder asyncDispatch(final MvcResult mvcResult):创建一个从启动异步处理的请求的MvcResult进行异步分派的RequestBuilder;


MockHttpServletRequestBuilder和MockMultipartHttpServletRequestBuilder API


MockHttpServletRequestBuilder header(String name, Object… values)/MockHttpServletRequestBuilder headers(HttpHeaders httpHeaders):添加头信息;

MockHttpServletRequestBuilder contentType(MediaType mediaType):指定请求的contentType头信息;

MockHttpServletRequestBuilder accept(MediaType… mediaTypes)/MockHttpServletRequestBuilder accept(String… mediaTypes):指定请求的Accept头信息;

MockHttpServletRequestBuilder content(byte[] content)/MockHttpServletRequestBuilder content(String content):指定请求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)/MockHttpServletRequestBuilder sessionAttrs(Map<string, object>sessionAttributes):设置请求session属性数据;

MockHttpServletRequestBuilder flashAttr(String name, Object value)/MockHttpServletRequestBuilder flashAttrs(Map<string, object> flashAttributes):指定请求的flash信息,比如重定向后的属性信息;

MockHttpServletRequestBuilder session(MockHttpSession session) :指定请求的Session;

MockHttpServletRequestBuilder principal(Principal principal) :指定请求的Principal;

MockHttpServletRequestBuilder contextPath(String contextPath) :指定请求的上下文路径,必须以“/”开头,且不能以“/”结尾;

MockHttpServletRequestBuilder pathInfo(String pathInfo) :请求的路径信息,必须以“/”开头;

MockHttpServletRequestBuilder secure(boolean secure):请求是否使用安全通道;

MockHttpServletRequestBuilder with(RequestPostProcessor postProcessor):请求的后处理器,用于自定义一些请求处理的扩展点;

MockMultipartHttpServletRequestBuilder继承自MockHttpServletRequestBuilder,又提供了如下API

MockMultipartHttpServletRequestBuilder file(String name, byte[] content)/MockMultipartHttpServletRequestBuilder file(MockMultipartFile file):指定要上传的文件;


ResultActions

调用MockMvc.perform(RequestBuilder requestBuilder)后将得到ResultActions,通过ResultActions完成如下三件事:


ResultActions andExpect(ResultMatcher matcher) :添加验证断言来判断执行请求后的结果是否是预期的;

ResultActions andDo(ResultHandler handler) :添加结果处理器,用于对验证成功后执行的动作,如输出下请求/结果信息用于调试;

MvcResult andReturn() :返回验证成功后的MvcResult;用于自定义验证/下一步的异步处理;


ResultMatcher/MockMvcResultMatchers


ResultMatcher用来匹配执行完请求后的结果验证,其就一个match(MvcResult result)断言方法,如果匹配失败将抛出相应的异常;
spring mvc测试框架提供了很多ResultMatchers来满足测试需求。注意这些ResultMatchers并不是ResultMatcher的子类,而是返回ResultMatcher实例的。
Spring mvc测试框架为了测试方便提供了MockMvcResultMatchers静态工厂方法方便操作;


MockMvcResultMatchers静态工厂方法


HandlerResultMatchers handler():请求的Handler验证器,比如验证处理器类型/方法名;此处的Handler其实就是处理请求的控制器;

RequestResultMatchers request():得到RequestResultMatchers验证器;

ModelResultMatchers model():得到模型验证器;

ViewResultMatchers view():得到视图验证器;

FlashAttributeResultMatchers flash():得到Flash属性验证;

StatusResultMatchers status():得到响应状态验证器;

HeaderResultMatchers header():得到响应Header验证器;

CookieResultMatchers cookie():得到响应Cookie验证器;

ContentResultMatchers content():得到响应内容验证器;

JsonPathResultMatchers jsonPath(String expression, Object … args)/ResultMatcher jsonPath(String expression, Matcher matcher):得到Json表达式验证器;

XpathResultMatchers xpath(String expression, Object... args)/XpathResultMatchers xpath(String expression, Map<String,String> namespaces, Object... args):得到Xpath表达式验证器;

ResultMatcher forwardedUrl(final String expectedUrl):验证处理完请求后转发的url(绝对匹配);

ResultMatcher forwardedUrlPattern(final String urlPattern):验证处理完请求后转发的url(Ant风格模式匹配,@since spring4);

ResultMatcher redirectedUrl(final String expectedUrl):验证处理完请求后重定向的url(绝对匹配);

ResultMatcher redirectedUrlPattern(final String expectedUrl):验证处理完请求后重定向的url(Ant风格模式匹配,@since spring4);