Spring Boot的单元测试
Spring Boot为测试提供了一个名为spring-boot-starter-test的Starter。我们使用STS创建Spring Boot应用时,将自动添加spring-boot-starter-test依赖。这样在测试时,就没有必要再添加额外的JAR包。spring-boot-starter-test主要提供了以下测试库:
1.Junit:标准的单元测试Java应用程序。
2.Spring Test&Spring Boot Test:针对Spring Boot应用程序的单元测试。
3.Mockito:Java mocking框架,用于模拟任何Spring管理的Bean,比如在单元测试中模拟一个第三方系统Service接口返回的数据,而不去真正调用第三方系统。
4.AssertJ:一个流畅的assertion库,同时也提供了更多的期望值与测试返回值的比较方式。
5.JSONassert:对JSON对象或JSON字符串断言的库。
6.JsonPath:提供类似Xpath(一门在XML文档中查找信息的语言)那样的符号来获取JSON数据片段。
一、 Spring Boot单元测试程序模板
@RunWith(SpringRunner.class)
@SpringBootTest
public class GoodsServiceTest {
//注入要测试的service
@Autowired
private GoodsService goodsService;
@Test
public void testGoodsService() {
//调用GoodsService的方法进行测试
}
}
@RunWith注解是JUnit标准的一个注解,目的是用来告诉JUnit框架不要使用内置的方式进行单元测试,而应使用@RunWith指明的类来进行单元测试,所有的Spring单元测试总是使用SpringRunner.class。
@SpringBootTest用于Spring Boot应用测试,它默认根据包名逐级往上找,一直找到Spring Boot主程序(包含@SpringBootApplication注解的类),并在单元测试时启动该主程序来创建Spring上下文环境。
二、 测试Service
单元测试Service代码与通过Controller调用Service代码相比,需要特别考虑该Service是否依赖其他还未开发完毕的Service(第三方接口)。如果依赖其他还未开发完毕的Service,我们需要使用Mockito来模拟未完成的Service。
假设,在UserService中依赖CreditService(第三方接口)的getCredit方法获得用户积分。
那么,我们如何测试UserService呢?问题是单元测试不能实际调用CreditService(因为CreditService是第三方系统),因此,我们在单元测试类需要使用Mockito的注解@MockBean自动注入Spring管理的Service,用来提供模拟实现,在Spring上下文中,CreditService实现已经被模拟实现代替了。
三、测试Controller
在Spring Boot应用中,可以单独测试Controller代码,用来验证与Controller相关的URL路径映射、文件上传、参数绑定、参数校验等特性。可以通过@WebMvcTest注解来完成Controller单元测试,当然也可以通过@SpringBootTest测试Controller。
需要注意的是,我们在使用@WebMvcTest注解测试Controller时,带有@Service以及别的注解组件类不会自动被扫描注册为Spring容器管理的Bean,而@SpringBootTest注解告诉Spring Boot去寻找一个主配置类(一个带@SpringBootApplication的类),并使用它来启动Spring应用程序上下文,注入所有Bean。另外,还需要注意的是,MockMvc用来在Servlet容器内对Controller进行单元测试,并未真正发起了HTTP请求调用Controller。
@WebMvcTest用于从服务器端对Controller层进行统一测试;如果需要从客户端与应用程序交互时,应该使用@SpringBootTest做集成测试。
四、模拟Controller请求
MockMvc的核心方法是:
public ResultActions perform(RequestBuilder requestBuilder)
RequestBuilder类可以通过调用MockMvcRequestBuilders的get、post、multipart等方法来模拟Controller请求,常用示例如下:
模拟一个get请求:
mvc.peform(get("/getCredit/{id}", uid));
模拟一个post请求:
mvc.peform(post("/getCredit/{id}", uid));
模拟文件上传:
mvc.peform(multipart("/upload").file("file", "文件内容".getBytes("UTF-8")));
模拟请求参数:
//模拟提交errorMessage参数
mvc.peform(get("/getCredit/{id}/{uname}", uid, uname).param("errorMessage", "用户名或密码错误"));
//模拟提交check
mvc.peform(get("/getCredit/{id}/{uname}", uid, uname).param("job", "收银员", "IT" ));
五、 比较Controller请求返回的结果
我们知道,MockMvc的perform方法返回ResultActions实例,这个实例代表了请求Controller返回的结果。它提供了一系列andExpect方法来对请求Controller返回的结果进行比较。
mvc.peform(get("/getOneUser/10"))
.andExpect(status().isOk()) //期望请求成功,即状态码为200
//期望返回内容是application/json
.andExpect(content().contentType(MediaType.APPLICATION_JSON))
//使用JsonPath比较返回的JSON内容
.andExpect(jsonPath("$.name").value("chenheng")); //检查返回内容
1.比较返回的视图
mvc.peform(get("/getOneUser/10"))
.andExpect(view().name("/userDetail"));
2.比较模型
mvc.peform(post("/addOneUser"))
.andExpect(status().isOk())
.andExpect(model().size(1))
.andExpect(model().attributeExists("oneUser"))
.andExpect(model().attribute("oneUser", "chenheng"))
3.比较转发或重定向
mvc.peform(post("/addOneUser"))
.andExpect(forwardedUrl("/user/selectAll")); //或者 redirectedUrl("/user/selectAll")
4.比较返回的内容
andExpect(content().string("测试很好玩")); //比较返回的字符串
andExpect(content().xml(xmlContent)); //返回内容是XML,并且与xmlContent(变量)一样
andExpect(content().json(jsonContent)); //返回内容是JSON,并且与jsonContent(变量)一样
六、 测试实例
【例9-2】使用@WebMvcTest和@SpringBootTest两种方式测试某一个控制器方法。
1.创建基于Spring Data JPA的Web应用ch9_2
2.修改pom.xml文件,引入MySQL依赖
3.配置数据库连接等基本属性
4.创建持久化实体类
5.创建数据访问层
6.创建控制器层
7.创建测试用例(@WebMvcTest和@SpringBootTest)
8.运行

