Spring Boot项目中使用Mockito

摘自:http://www.javashuo.com/article/p-xipbqfea-u.htmlhtml

Spring Boot项目中使用Mockito

 

本文首发于我的网站:Spring Boot项目中使用Mockitojava

Spring Boot能够和大部分流行的测试框架协同工做:经过Spring JUnit建立单元测试;生成测试数据初始化数据库用于测试;Spring Boot能够跟BDD(Behavier Driven Development)工具、Cucumber和Spock协同工做,对应用程序进行测试。git

进行软件开发的时候,咱们会写不少代码,不过,再过六个月(甚至一年以上)你知道本身的代码怎么运做么?经过测试(单元测试、集成测试、接口测试)能够保证系统的可维护性,当咱们修改了某些代码时,经过回归测试能够检查是否引入了新的bug。总得来讲,测试让系统再也不是一个黑盒子,让开发人员确认系统可用。程序员

在web应用程序中,对Controller层的测试通常有两种方法:(1)发送http请求;(2)模拟http请求对象。第一种方法须要配置回归环境,经过修改代码统计的策略来计算覆盖率;第二种方法是比较正规的思路,可是在我目前经历过的项目中用得很少,今天总结下如何用Mock对象测试Controller层的代码。github

在以前的几篇文章中,咱们都使用bookpub这个应用程序做为例子,今天也不例外,准备测试它提供的RESTful接口是否能返回正确的响应数据。这种测试不一样于单元测试,须要为之初始化完整的应用程序上下文、全部的spring bean都织入以及数据库中须要有测试数据,通常来讲这种测试称之为集成测试或者接口测试web

实战

经过spirng.io新建的Spring Boot项目提供了一个空的测试文件——BookPubApplicationTest.java,内容是:面试

@RunWith(SpringJUnit4ClassRunner.class) @SpringApplicationConfiguration(classes = BookPubApplication.class) public class BookPubApplicationTests { @Test public void contextLoads() { } }
  • 在pom文件中增长spring-boot-starter-test依赖,添加jsonPath依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>com.jayway.jsonpath</groupId> <artifactId>json-path</artifactId> </dependency>
  • 在BookPubApplicationTest中添加测试用例
package com.test.bookpub; import com.test.bookpub.domain.Book; import com.test.bookpub.repository.BookRepository; import org.junit.Before;import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.test.SpringApplicationConfiguration; import org.springframework.boot.test.TestRestTemplate; import org.springframework.boot.test.WebIntegrationTest; import org.springframework.http.MediaType; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.setup.MockMvcBuilders; import org.springframework.web.client.RestTemplate; import org.springframework.web.context.WebApplicationContext; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.hamcrest.Matchers.containsString; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @RunWith(SpringJUnit4ClassRunner.class) @SpringApplicationConfiguration(classes = BookPubApplication.class) @WebIntegrationTest("server.port:0") public class BookPubApplicationTests { @Autowired private WebApplicationContext context; @Autowired private BookRepository bookRepository; @Value("${local.server.port}") private int port; private MockMvc mockMvc; private RestTemplate restTemplate = new TestRestTemplate(); @Before public void setupMockMvc() { mockMvc = MockMvcBuilders.webAppContextSetup(context).build(); } @Test public void contextLoads() { assertEquals(1, bookRepository.count()); } @Test public void webappBookIsbnApi() { Book book = restTemplate.getForObject("http://localhost:" + port +"/books/9876-5432-1111", Book.class); assertNotNull(book); assertEquals("中文测试", book.getPublisher().getName()); } @Test public void webappPublisherApi() throws Exception { //MockHttpServletRequestBuilder.accept方法是设置客户端可识别的内容类型 //MockHttpServletRequestBuilder.contentType,设置请求头中的Content-Type字段,表示请求体的内容类型 mockMvc.perform(get("/publishers/1") .accept(MediaType.APPLICATION_JSON_UTF8)) .andExpect(status().isOk()) .andExpect(content().string(containsString("中文测试"))) .andExpect(jsonPath("$.name").value("中文测试")); } }
  • spring boot项目的代码覆盖率
    使用cobertura,参考项目的github地址:spring boot template
# To create test coverage reports (in target/site/cobertura) mvn clean cobertura:cobertura test

cobertura统计代码覆盖率
单击某个类进去,能够看到详细信息

分析

首先分析在BookPubApplicationTests类中用到的注解:spring

  • @RunWith(SpringJUnit4ClassRunner.class),这是JUnit的注解,经过这个注解让SpringJUnit4ClassRunner这个类提供Spring测试上下文。
  • @SpringApplicationConfiguration(classes = BookPubApplication.class),这是Spring Boot注解,为了进行集成测试,须要经过这个注解加载和配置Spring应用上下文。这是一个元注解(meta-annoation),它包含了@ContextConfiguration( loader = SpringApplicationContextLoader.class)这个注解,测试框架经过这个注解使用Spring Boot框架的SpringApplicationContextLoader加载器建立应用上下文。
  • @WebIntegrationTest("server.port:0"),这个注解表示当前的测试是集成测试(integration test),所以须要初始化完整的上下文并启动应用程序。这个注解通常和@SpringApplicationConfiguration一块儿出现。server.port:0指的是让Spring Boot在随机端口上启动Tomcat服务,随后在测试中程序经过@Value("${local.server.port}")得到这个端口号,并赋值给port变量。当在Jenkins或其余持续集成服务器上运行测试程序时,这种随机获取端口的能力能够提供测试程序的并行性。

了解完测试类的注解,再看看测试类的内部。因为这是Spring Boot的测试,所以咱们可经过@Autowired注解织入任何由Spring管理的对象,或者是经过@Value设置指定的环境变量的值。在如今这个测试类中,咱们定义了WebApplicationContextBookRepository对象。shell

每一个测试用例用@Test注解修饰。在第一个测试用例——contextLoads()方法中,我仅仅须要确认BookRepository链接已经创建,而且数据库中已经包含了对应的测试数据。数据库

第二个测试用例用来测试咱们提供的RESTful URL——经过ISBN查询一本书,即“/books/{isbn}”。在这个测试用例中咱们使用TestRestTemplate对象发起RESTful请求。

第三个测试用例中展现了如何经过MockMvc对象实现跟第二个测试相似的功能。Spring测试框架提供MockMvc对象,能够在不须要客户端-服务端请求的状况下进行MVC测试,彻底在服务端这边就能够执行Controller的请求,跟启动了测试服务器同样。

测试开始以前须要创建测试环境,setup方法被@Before修饰。经过MockMvcBuilders工具,使用WebApplicationContext对象做为参数,建立一个MockMvc对象。

MockMvc对象提供一组工具函数用来执行assert判断,都是针对web请求的判断。这组工具的使用方式是函数的链式调用,容许程序员将多个测试用例连接在一块儿,并进行多个判断。在这个例子中咱们用到下面的一些工具函数:

  • perform(get(...))创建web请求。在咱们的第三个用例中,经过MockMvcRequestBuilder执行GET请求。
  • andExpect(...)能够在perform(...)函数调用后屡次调用,表示对多个条件的判断,这个函数的参数类型是ResultMatcher接口,在MockMvcResultMatchers这这个类中提供了不少返回ResultMatcher接口的工具函数。这个函数使得能够检测同一个web请求的多个方面,包括HTTP响应状态码(response status),响应的内容类型(content type),会话中存放的值,检验重定向、model或者header的内容等等。这里须要经过第三方库json-path检测JSON格式的响应数据:检查json数据包含正确的元素类型和对应的值,例如jsonPath("$.name").value("中文测试")用于检查在根目录下有一个名为name的节点,而且该节点对应的值是“中文测试”。

一个字符乱码问题

  • 问题描述:经过spring-boot-starter-data-rest创建的repository,取出的汉字是乱码。

  • 分析:使用postman和httpie验证都没问题,说明是Mockmvc的测试用例写得不对,应该主动设置客户端如何解析HTTP响应,用get.accept方法设置客户端可识别的内容类型,修改后的测试用例以下:

@Test public void webappPublisherApi() throws Exception { //MockHttpServletRequestBuilder.accept方法是设置客户端可识别的内容类型 //MockHttpServletRequestBuilder.contentType,设置请求头中的Content-Type字段,表示请求体的内容类型 mockMvc.perform(get("/publishers/1") .accept(MediaType.APPLICATION_JSON_UTF8)) .andExpect(status().isOk()) .andExpect(content().string(containsString("中文测试"))) .andExpect(jsonPath("$.name").value("中文测试")); }

参考资料

  1. 基于Spring-WS的Restful API的集成测试
  2. J2EE要懂的小事—图解HTTP协议
  3. Integration Testing a Spring Boot Application
  4. spring boot project template

Spring Boot 1.x系列

  1. Spring Boot的自动配置、Command-line-Runner
  2. 了解Spring Boot的自动配置
  3. Spring Boot的@PropertySource注解在整合Redis中的使用
  4. Spring Boot项目中如何定制HTTP消息转换器
  5. Spring Boot整合Mongodb提供Restful接口
  6. Spring中bean的scope
  7. Spring Boot项目中使用事件派发器模式
  8. Spring Boot提供RESTful接口时的错误处理实践
  9. Spring Boot实战之定制本身的starter
  10. Spring Boot项目如何同时支持HTTP和HTTPS协议
  11. 自定义的Spring Boot starter如何设置自动配置注解

本号专一于后端技术、JVM问题排查和优化、Java面试题、我的成长和自我管理等主题,为读者提供一线开发者的工做和成长经验,期待你能在这里有所收获。
javaadu

 
分类:  C/C++
标签:  springbootJava
相关文章
相关标签/搜索