SpringBoot进行单元测试

概述

在本文中,我们将看看如何使用Spring Boot中的框架支持编写测试。 我们将介绍可以单独运行的单元测试以及在执行测试之前引导Spring上下文的集成测试。

工程安装

我们将在本文中使用的应用程序是一个API,它提供了一些员工资源的基本操作。 这是一个典型的分层架构 - API调用从Controller到Service到Persistence层。

Maven 依赖

1
2
3
4
5
6
7
8
9
10
11
12
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<version>1.5.3.RELEASE</version>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>test</scope>
<version>1.4.194</version>
</dependency>

用@DataJpaTest 测试持久层

示例实体

1
2
3
4
5
6
7
8
9
10
11
12
13
@Entity
@Table(name = "person")
public class Employee {

@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;

@Size(min = 3, max = 20)
private String name;

// standard getters and setters, constructors
}

持久库

1
2
3
4
5
6
@Repository
public interface EmployeeRepository extends JpaRepository<Employee, Long> {

public Employee findByName(String name);

}

单元测试示便

1
2
3
4
5
6
7
8
9
10
11
12
13
@RunWith(SpringRunner.class)
@DataJpaTest
public class EmployeeRepositoryTest {

@Autowired
private TestEntityManager entityManager;

@Autowired
private EmployeeRepository employeeRepository;

// write test cases here

}

测试函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Test
public void whenFindByName_thenReturnEmployee() {
// given
Employee alex = new Employee("alex");
entityManager.persist(alex);
entityManager.flush();

// when
Employee found = employeeRepository.findByName(alex.getName());

// then
assertThat(found.getName())
.isEqualTo(alex.getName());
}

用@MockBean 测试服务层

示例服务层

1
2
3
4
5
6
7
8
9
10
11
@Service
public class EmployeeServiceImpl implements EmployeeService {

@Autowired
private EmployeeRepository employeeRepository;

@Override
public Employee getEmployeeByName(String name) {
return employeeRepository.findByName(name);
}
}

测试用例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@RunWith(SpringRunner.class)
public class EmployeeServiceImplTest {

@TestConfiguration
static class EmployeeServiceImplTestContextConfiguration {

@Bean
public EmployeeService employeeService() {
return new EmployeeServiceImpl();
}
}

@Autowired
private EmployeeService employeeService;

@MockBean
private EmployeeRepository employeeRepository;

// write test cases here
}

测试函数:

1
2
3
4
5
6
7
8
@Test
public void whenValidName_thenEmployeeShouldBeFound() {
String name = "alex";
Employee found = employeeService.getEmployeeByName(name);

assertThat(found.getName())
.isEqualTo(name);
}

用@WebMvcTest 测试web层

web层示例

1
2
3
4
5
6
7
8
9
10
11
12
@RestController
@RequestMapping("/api")
public class EmployeeRestController {

@Autowired
private EmployeeService employeeService;

@GetMapping("/employees")
public List<Employee> getAllEmployees() {
return employeeService.getAllEmployees();
}
}

测试用例:

1
2
3
4
5
6
7
8
9
10
11
12
@RunWith(SpringRunner.class)
@WebMvcTest(EmployeeRestController.class)
public class EmployeeRestControllerTest {

@Autowired
private MockMvc mvc;

@MockBean
private EmployeeService service;

// write test cases here
}

测试函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Test
public void givenEmployees_whenGetEmployees_thenReturnJsonArray()
throws Exception {

Employee alex = new Employee("alex");

List<Employee> allEmployees = Arrays.asList(alex);

given(service.getAllEmployees()).willReturn(allEmployees);

mvc.perform(get("/api/employees")
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(jsonPath("$", hasSize(1)))
.andExpect(jsonPath("$[0].name", is(alex.getName())));
}

用@SpringBootTest进行集成测试

tips

1
2
3
@RunWith(SpringJUnit4ClassRunner.class) // SpringJUnit支持,由此引入Spring-Test框架支持 
@SpringApplicationConfiguration(classes = SpringBootSampleApplication.class) // 指定我们SpringBoot工程的Application启动类
@WebAppConfiguration // 由于是Web项目,Junit需要模拟ServletContext,因此我们需要给我们的测试类加上@WebAppConfiguration。

结论

在本教程中,我们深入介绍了Spring Boot中的测试支持,并展示了如何高效编写单元测试。
本文的完整源代码可以在GitHub上找到。源代码包含更多的示例和各种测试用例。

参考