Spring Boot能够和大部分流行的测试框架协同工做:经过Spring JUnit建立单元测试;生成测试数据初始化数据库用于测试;Spring Boot能够跟BDD(Behavier Driven Development)工具、Cucumber和Spock协同工做,对应用程序进行测试。java
进行软件开发的时候,咱们会写不少代码,不过,再过六个月(甚至一年以上)你知道本身的代码怎么运做么?经过测试(单元测试、集成测试、接口测试)能够保证系统的可维护性,当咱们修改了某些代码时,经过回归测试能够检查是否引入了新的bug。总得来讲,测试让系统再也不是一个黑盒子,让开发人员确认系统可用。git
在web应用程序中,对Controller层的测试通常有两种方法:(1)发送http请求;(2)模拟http请求对象。第一种方法须要配置回归环境,经过修改代码统计的策略来计算覆盖率;第二种方法是比较正规的思路,可是在我目前经历过的项目中用得很少,今天总结下如何用Mock对象测试Controller层的代码。程序员
在以前的几篇文章中,咱们都使用bookpub这个应用程序做为例子,今天也不例外,准备测试它提供的RESTful接口是否能返回正确的响应数据。这种测试不一样于单元测试,须要为之初始化完整的应用程序上下文、全部的spring bean都织入以及数据库中须要有测试数据,通常来讲这种测试称之为集成测试或者接口测试。github
How Doweb
经过spirng.io新建的Spring Boot项目提供了一个空的测试文件——BookPubApplicationTest.java,内容是:spring
@RunWith(SpringJUnit4ClassRunner.class)数据库
@SpringApplicationConfiguration(classes = BookPubApplication.class)json
public class BookPubApplicationTests {服务器
@Testmvc
public void contextLoads() {
}
}
<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>
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("中文测试"));
}
}
使用cobertura,参考项目的github地址:spring boot template
# To create test coverage reports (in target/site/cobertura)
mvn clean cobertura:cobertura test
cobertura统计代码覆盖率
单击某个类进去,能够看到详细信息
分析
首先分析在BookPubApplicationTests类中用到的注解:
了解完测试类的注解,再看看测试类的内部。因为这是Spring Boot的测试,所以咱们可经过@Autowired注解织入任何由Spring管理的对象,或者是经过@Value设置指定的环境变量的值。在如今这个测试类中,咱们定义了WebApplicationContext和BookRepository对象。
每一个测试用例用@Test注解修饰。在第一个测试用例——contextLoads()方法中,我仅仅须要确认BookRepository链接已经创建,而且数据库中已经包含了对应的测试数据。
第二个测试用例用来测试咱们提供的RESTful URL——经过ISBN查询一本书,即“/books/{isbn}”。在这个测试用例中咱们使用TestRestTemplate对象发起RESTful请求。
第三个测试用例中展现了如何经过MockMvc对象实现跟第二个测试相似的功能。Spring测试框架提供MockMvc对象,能够在不须要客户端-服务端请求的状况下进行MVC测试,彻底在服务端这边就能够执行Controller的请求,跟启动了测试服务器同样。
测试开始以前须要创建测试环境,setup方法被@Before修饰。经过MockMvcBuilders工具,使用WebApplicationContext对象做为参数,建立一个MockMvc对象。
MockMvc对象提供一组工具函数用来执行assert判断,都是针对web请求的判断。这组工具的使用方式是函数的链式调用,容许程序员将多个测试用例连接在一块儿,并进行多个判断。在这个例子中咱们用到下面的一些工具函数:
一个字符乱码问题
@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的集成测试
3 Integration Testing a Spring Boot Application
4 spring boot project template
further readings