1. 概述
ReflectionTestUtils是Spring Test Context框架的一部分。它是单元测试中使用的基于反射的工具方法的集合,以及用于设置非public字段、调用非public方法和注入依赖项的集成测试方案。
在本教程中,我们将通过几个示例学习如何在单元测试中使用ReflectionTestUtils。
2. Maven依赖
首先让我们将所有必要依赖项添加到我们的pom.xml中:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.1.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.1.2.RELEASE</version>
<scope>test</scope>
</dependency>
可以从Maven中央仓库下载最新的spring-context和spring-test依赖项。
3. 使用ReflectionTestUtils为非Public字段赋值
假设我们需要在单元测试中使用具有private字段而没有公共setter方法的类的实例:
public class Employee {
private Integer id;
private String name;
// standard getters/setters
}
通常我们无法访问私有字段id来分配用于测试的值,因为它没有公共setter方法。
因此我们将使用ReflectionTestUtils.setField()方法为私有成员id赋值:
@Test
void whenNonPublicField_thenReflectionTestUtilsSetField() {
Employee employee = new Employee();
ReflectionTestUtils.setField(employee, "id", 1);
assertEquals(1, employee.getId());
}
4. 使用ReflectionTestUtils调用非Public方法
现在假设我们在Employee类中有一个私有方法employeeToString():
private String employeeToString() {
return "id: " + getId() + "; name: " + getName();
}
我们可以为employeeToString()方法编写一个单元测试,如下所示,即使它没有来自Employee类外部的任何访问权限:
@Test
public void whenNonPublicMethod_thenReflectionTestUtilsInvokeMethod() {
Employee employee = new Employee();
ReflectionTestUtils.setField(employee, "id", 1);
employee.setName("Smith, John");
assertEquals("id: 1; name: Smith, John", ReflectionTestUtils.invokeMethod(employee, "employeeToString"));
}
5. 使用ReflectionTestUtils注入依赖bean
假设我们想为以下带有@Autowired注解的private字段的Spring组件编写单元测试:
@Component
public class EmployeeService {
@Autowired
private HRService hrService;
public String findEmployeeStatus(Integer employeeId) {
return "Employee " + employeeId + " status: " + hrService.getEmployeeStatus(employeeId);
}
}
现在我们可以实现HRService组件,如下所示:
@Component
public class HRService {
public String getEmployeeStatus(Integer employeeId) {
return "Inactive";
}
}
此外,我们将使用Mockito为HRService类创建一个mock实现。我们将这个HRService mock注入到EmployeeService实例中,并在我们的单元测试中使用它:
HRService hrService = Mockito.mock(HRService.class);
Mockito.when(hrService.getEmployeeStatus(employee.getId())).thenReturn("Active");
因为hrService是一个没有公共setter方法的private字段,我们将使用ReflectionTestUtils.setField()方法将我们上面创建的mock注入到这个private字段中。
EmployeeService employeeService = new EmployeeService();
ReflectionTestUtils.setField(employeeService, "hrService", hrService);
最后,我们的单元测试将如下所示:
@Test
void whenInjectingMockOfDependency_thenReflectionTestUtilsSetField() {
Employee employee = new Employee();
ReflectionTestUtils.setField(employee, "id", 1);
employee.setName("Smith, John");
HRService hrService = Mockito.mock(HRService.class);
Mockito.when(hrService.getEmployeeStatus(employee.getId())).thenReturn("Active");
EmployeeService employeeService = new EmployeeService();
ReflectionTestUtils.setField(employeeService, "hrService", hrService);
assertEquals("Employee " + employee.getId() + " status: Active", employeeService.findEmployeeStatus(employee.getId()));
}
我们应该注意到,这种技术是我们在bean类中使用字段注入这一事实的解决方法。如果我们切换到构造函数注入,那么这种方法就没有必要了。
6. 总结
在本文中,我们通过几个示例演示了如何在单元测试中使用ReflectionTestUtils。
与往常一样,本教程的完整源代码可在GitHub上获得。