1。前言大多数在Java开发中接触的开发者都不太重视接口测试,导致联调对接出现各种问题。有的还使用Postman等工具进行测试。虽然在使用上没有问题,但是如果接口加上权限测试,那就恶心了。因此,建议在单元测试中对接口进行测试,保证接口在交付前的健壮性。今天分享一下胖哥在开发过程中是如何测试SpringMVC接口的。在开始之前,请确保添加SpringBootTest相关组件。最新版本应包含以下依赖项:org.springframework.bootspring-boot-starter-testtestorg.junit.vintagejunit-vintage-engine本文在SpringBoot2.3.4下进行。发布。2.单独测试控制层如果我们只需要测试控制层接口(Controller),而该接口不依赖@Service、@Component等注解声明的SpringBean,我们可以使用@WebMvcTest仅启用Web控制层测试,如@WebMvcTestclassCustomSpringInjectApplicationTests{@AutowiredMockMvcmockMvc;@SneakyThrows@TestvoidcontextLoads(){mockMvc.perform(MockMvcRequestBuilders.get("/foo/map")).andExpect(ResultMatcher.matchAll(status().isOk(),content().contentType(MediaType.APPLICATION_JSON),jsonPath("$.test",Is.is("hello")))).andDo(MockMvcResultHandlers.print());}}这个方法就多了更快,它只加载应用程序的一小部分。但是如果涉及到服务层,这个方法就没有效果了,需要我们整体测试一下。3、整体测试SpringBoot下的接口测试大多是整体的、综合的测试,涉及到控制层、服务层、持久层等各个方面,所以需要加载一个比较完整的SpringBoot上下文。此时我们可以通过声明一个抽象测试基类来做到这一点:.springframework.boot.test.context.SpringBootTest;importorg.springframework.test.web.servlet.MockMvc;/***测试基类,*@authorfelord.cn*/@SpringBootTest@AutoConfigureMockMvcabstractclassCustomSpringInjectApplicationTests{/***TheMockmvc.*/@AutowiredMockMvcmockMvc;//其他公共依赖及处理方式}只有@AutoConfigureMockMvc存在时,MockMvc才会被注入到SpringIoC中。然后针对具体的控制层编写如下测试代码:packagecn.felord.custom;importlombok.SneakyThrows;importorg.hamcrest.core.Is;importorg.junit.jupiter.api.Test;importorg.springframework.http.MediaType;importorg。springframework.test.web.servlet.ResultMatcher;importorg.springframework.test.web.servlet.request.MockMvcRequestBuilders;importorg.springframework.test.web.servlet.result.MockMvcResultHandlers;importstaticorg.springframework.test.web.servlet.result。MockMvcResultMatchers.*;/***测试FooController.**@authorfelord.cn*/publicclassFooTestsextendsCustomSpringInjectApplicationTests{/***/foo/map接口测试.*/@SneakyThrows@TestvoidcontextLoads(){mockMvc.perform(MockMvcRequestBuilders.get("/foo/map")).andExpect(ResultMatcher.matchAll(status().isOk(),content().contentType(MediaType.APPLICATION_JSON),jsonPath("$.test",Is.is("bar")))).andDo(MockMvcResultHandlers.print());}}4.MockMvc在测试集成测试时,希望通过输入URL来测试Controller。如果启动服务器并建立http客户端进行测试,这将使测试成为很麻烦。比如启动速度慢,测试验证不方便,依赖网络环境等。为了测试Controller,引入了MockMvc来模拟Http请求。可以直接使用网络的形式,转化为Controller。调用,可以使测试速度快,独立于网络环境,并提供一套验证工具,可以使请求的验证统一方便。接下来我们一步步构造一个测试mock请求。假设我们有一个如下所示的接口:@RestController@RequestMapping("/foo")publicclassFooController{@AutowiredprivateMyBeanmyBean;@GetMapping("/user")publicMapbar(@RequestHeader("Api-Version")StringapiVersion,Useruser){Mapma??p=newHashMap<>();map.put("test",myBean.bar());map.put("version",apiVersion);map.put("username",user.getName());//todoyourbusinessreturnmap;}}参数设置为name=felord.cn&age=18,那么对应的HTTP报文如下:GET/foo/user?name=felord.cn&age=18HTTP/1.1Host:localhost:8888Api-Version:v1可预测的返回值为:{"test":"bar","version":"v1","username":"felord.cn"}在其实接口的测试可以分为以下几个步骤。构建请求构建请求由MockMvcRequestBuilders负责,它提供了所有请求的请求方法(Method)、请求头(Header)、请求体(Body)、参数(Parameters)、会话(Session)等属性构造。/foo/user接口的请求可以转化为:MockMvcRequestBuilders.get("/foo/user").param("name","felord.cn").param("age","18").header("Api-Version","v1")执行Mock请求,然后MockMvc执行Mock请求:mockMvc.perform(MockMvcRequestBuilders.get("/foo/user").param("name","felord.cn").param("age","18").header("Api-Version","v1"))processingtheresult请求结果被封装到ResultActions对象中,它封装了多种方法让我们可以处理模拟请求结果。期待结果ResultActions#andExpect(ResultMatchermatcher)方法负责预测响应的结果,看是否符合测试的预期值。参数ResultMatcher负责从response对象中提取出我们需要期望的部分进行期望比较。如果我们希望接口/foo/user返回JSON,HTTP状态为200,响应体中包含version=v1的值,我们应该这样声明:ResultMatcher.matchAll(MockMvcResultMatchers.status().isOk(),MockMvcResultMatchers.content().contentType(MediaType.APPLICATION_JSON),MockMvcResultMatchers.jsonPath("$.version",Is.is("v1")));JsonPath是一个强大的JSON解析库,请使用它的项目仓库https://github.com/json-path/JsonPath来了解。处理响应。ResultActions#andDo(ResultHandlerhandler)方法负责打印整个请求/响应或日志输出和流输出。这些方法由MockMvcResultHandlers工具类提供。我们可以通过以上三种方式查看请求响应的详细信息。例如/foo/用户界面:MockHttpServletRequest:HTTPMethod=GETRequestURI=/foo/userParameters={name=[felord.cn],age=[18]}Headers=[Api-Version:"v1"]Body=nullSessionAttrs={处理程序:Type=cn.felord.xbean.config.FooControllerMethod=cn.felord.xbean.config.FooController#urlEncode(String,Params)Async:Asyncstarted=falseAsyncresult=nullResolvedException:Type=nullModelAndView:Viewname=nullView=nullModel=nullFlashMap:Attributes=nullMockHttpServletResponse:Status=200Errormessage=nullHeaders=[Content-Type:"application/json"]Contenttype=application/jsonBody={"test":"bar","version":"v1","username":"felord.cn"}ForwardedURL=nullRedirectedURL=nullCookies=[]获取返回结果如果想对响应的结果做进一步处理,也可以通过ResultActions#andReturn()获取MvcResult类型的结果进行进一步处理。完整的测试过程通常andExpect是我们必然会选择的,而andDo和andReturn在某些场景下会有用,它们是可选的。我们将以上内容联系在一起。@AutowiredMockMvcmockMvc;@SneakyThrows@TestvoidcontextLoads(){mockMvc.perform(MockMvcRequestBuilders.get("/foo/user").param("name","felord.cn").param("age","18").header("Api-Version","v1")).andExpect(ResultMatcher.matchAll(status().isOk(),content().contentType(MediaType.APPLICATION_JSON),jsonPath("$.version",Is.is("v1")))).andDo(MockMvcResultHandlers.print());}这种流式接口单元测试在语义上也比较容易理解。您可以使用各种断言、正例和反例来测试您的界面,最终使您的界面更加健壮。5.小结一旦你熟练掌握了这种方法,你写出的界面就会更加权威,不会漏洞百出。有时甚至可以使用Mock来设计界面,使其更适合业务。所以,CRUD并不是完全没有技术含量。优质高效的CRUD往往需要这种工程化的单元测试来支撑。本文转载自微信公众号“码农小胖哥”,可通过以下二维码关注。转载本文请联系码农小胖公众号。