本文基于springboot 2.4.5介绍基于spring-boot-starter-test进行单元测试的基本方法。
1. 普通方式测试service层及普通Bean
2. mock方式测试controller层
目录
1. 单元测试范围
controller,service,核心Bean(重要算法,重要工具类)
2. 搭建测试项目引入maven库
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
3. 待测Service及Controller代码如下
代码结构如下:
User.java
public class User {
private int id;
private String username;
private String password;
/*
constructor
getter
setter
*/
}
UserService.java
public interface UserService {
public User getUser(int id);
public List<User> getUsersBySubname(String subname);
}
包含两个待测函数,一个是获取单个实体,一个获取实体集合。
UserServiceImpl.java
@Service
public class UserServiceImpl implements UserService {
@Override
public User getUser(int id) {
// 这里模拟一下数据库查询
User user = new User(1, "user1", "user1");
return user;
}
@Override
public List<User> getUsersBySubname(String subname) {
List<User> users = new ArrayList<>();
users.add(new User(1, "user1", "user1"));
users.add(new User(2, "user2", "user2"));
return users;
}
}
UserController.java
@RestController
@RequestMapping("/")
public class UserController {
@Autowired
private UserService userService;
public UserController(UserService userService) {
this.userService = userService;
}
@GetMapping("/user/{id}")
public User getUser(@PathVariable int id) {
User user = userService.getUser(1);
return user;
}
@GetMapping("/users")
public List<User> getUsers(@RequestParam String subname) {
List<User> users = userService.getUsersBySubname(subname);
return users;
}
}
同样包含两个待测Controller,一个是传入路径参数,一个传入请求参数。为后面单元测试做不同参数的传入做准备。
运行查看服务是否正常,结果如下:
4. 编写Service测试类
新建Test文件,存放路径如下,命名推荐以待测类名称+Test命名。
单元测试类上注解:@SpringBootTest,表明这是一个SpringBoot的单元测试类。
待测类上注解:@Autowired,因为都还是基于Spring容器管理,所以依然使用自动装配获取待测对象。
单元测试函数上注解:@Test,表明这是个单元测试方法。里面写要测试的逻辑,之后用assertXxxx()来判断输出是否符合预期。
具体代码如下:
@SpringBootTest
class UserServieTest {
@Autowired
private UserService userService;
@Test
void getUserTest() {
User user = userService.getUser(1);
assertNotNull(user);
}
@Test
void getUsersTest() {
List<User> users = userService.getUsersBySubname("user");
assertEquals(users.size(), 3);
}
}
该文件中右键,Run as junit test,即可执行查看结果。
全部通过则显示绿色。
否则显示红色,并标出错误Test
5. 编写Controller测试类
Controller因为主要是处理网络请求的,这里测试需要模拟网络请求作为输入,传统的方式有可以通过RestTemlate进行测试。这里主要介绍MockMvc的方式模拟请求。
Spring测试框架提供MockMvc对象,可以在不需要客户端-服务端请求的情况下进行MVC测试,完全在服务端这边就可以执行Controller的请求,跟启动了测试服务器一样。
测试开始之前需要建立测试环境,setup方法被@Before修饰。通过MockMvcBuilders工具,使用WebApplicationContext对象作为参数,创建一个MockMvc对象。
然后基于mockMvc对象进行请求模拟和结果断言。
具体代码如下:
package com.daicy.test.controller;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
/**
* @author daicy
*/
@SpringBootTest
public class UserControllerTest {
MockMvc mockMvc;
@Autowired
WebApplicationContext wc;
@BeforeEach
public void beforeSetUp() {
this.mockMvc = MockMvcBuilders.webAppContextSetup(wc).build();
}
@Test
void getUserByIdTest() throws Exception {
this.mockMvc.perform(
MockMvcRequestBuilders.get("/user/{id}", "1"))
.andDo(print())
.andExpect(status().isOk())
.andExpect(jsonPath("$.id").value("1"))
.andExpect(jsonPath("$.username").isNotEmpty())
.andReturn();
}
@Test
void getUsersTest() throws Exception {
this.mockMvc.perform(
MockMvcRequestBuilders.get("/users")
.param("subname", "user"))
.andDo(print())
.andExpect(status().isOk())
.andExpect(jsonPath("$").isNotEmpty())
.andReturn();
}
}
其中,路径参数直接作为请求函数参数写入,请求参数需要链式一个param(key, value)。
andExpect负责做结果断言,其中Restapi返回的Json信息,用jsonPath可以解析。
断言方式还有很多,可以根据需要自行选取。
最后andReturn()可以返回MvcResult,里面有更详细的请求和响应信息,具体可以看由andDo(print())在控制台打印出的信息:
如果需要更详细的信息判断,可以把MvcResult接回来,然后基于assertXxx继续判断内部详细信息。
至此,简单的Controller和Service单元测试介绍完毕。