Guide to ReflectionTestUtils for Unit Testing – 用于单元测试的ReflectionTestUtils指南

最后修改: 2018年 11月 18日

中文/混合/英文(键盘快捷键:t)

1. Introduction

1.介绍

ReflectionTestUtils is a part of the Spring Test Context framework. It’s a collection for reflection-based utility methods used in a unit, and integration testing scenarios to set the non-public fields, invoke non-public methods, and inject dependencies.

ReflectionTestUtils是Spring Test Context框架的一部分。它是一个基于反射的实用方法的集合,在单元和集成测试场景中用于设置非公共字段、调用非公共方法和注入依赖。

In this tutorial, we’ll learn how to use ReflectionTestUtils in unit testing by going through several examples.

在本教程中,我们将通过几个例子来学习如何在单元测试中使用ReflectionTestUtils

2. Maven Dependencies

2.Maven的依赖性

We’ll start by adding the latest versions of all the necessary dependencies to our pom.xml:

我们将首先把所有必要的依赖项的最新版本添加到我们的 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>

The latest spring-context and spring-test dependencies can be downloaded from the Maven Central repository.

最新的spring-contextspring-test依赖项可以从Maven Central仓库下载。

3. Using ReflectionTestUtils to Set a Value of a Non-Public Field

3.使用ReflectionTestUtils来设置一个非公共字段的值

Suppose we need to use an instance of a class having a private field, without a public setter method in our unit test.

假设我们需要在单元测试中使用一个具有私有字段的类的实例,而没有一个公共的setter方法。

We’ll start by creating it:

我们将从创建它开始。

public class Employee {
    private Integer id;
    private String name;

    // standard getters/setters
}

Normally we can’t access the private field id to assign a value for testing because there isn’t a public setter method for it.

通常我们不能访问私有字段id来为测试赋值,因为它没有一个公共的setter方法。

So we’ll use the ReflectionTestUtils.setField method to assign a value to the private member id:

所以我们将使用ReflectionTestUtils.setField方法来给私有成员id赋值。

@Test
public void whenNonPublicField_thenReflectionTestUtilsSetField() {
    Employee employee = new Employee();
    ReflectionTestUtils.setField(employee, "id", 1);
 
    assertTrue(employee.getId().equals(1));
}

4. Using ReflectionTestUtils to Invoke a Non-Public Method

4.使用ReflectionTestUtils来调用一个非公共方法

Now let’s imagine that we have a private method, employeeToString, in the Employee class:

现在让我们想象一下,我们在Employee类中有一个私有方法,employeeToString,

private String employeeToString(){
    return "id: " + getId() + "; name: " + getName();
}

We can write a unit test for the employeeToString method as below, even though it doesn’t have any access from outside of an Employee class:

我们可以为employeeToString方法写一个单元测试,如下所示,尽管它没有任何来自Employee类外部的访问。

@Test
public void whenNonPublicMethod_thenReflectionTestUtilsInvokeMethod() {
    Employee employee = new Employee();
    ReflectionTestUtils.setField(employee, "id", 1);
    employee.setName("Smith, John");
 
    assertTrue(ReflectionTestUtils.invokeMethod(employee, "employeeToString")
      .equals("id: 1; name: Smith, John"));
}

5. Using ReflectionTestUtils to Inject Dependencies

5.使用ReflectionTestUtils来注入依赖关系

Let’s say we want to write a unit test for the following Spring component having a private field with the @Autowired annotation:

假设我们想为下面这个拥有@Autowired注解的私有字段的Spring组件写一个单元测试。

@Component
public class EmployeeService {
 
    @Autowired
    private HRService hrService;

    public String findEmployeeStatus(Integer employeeId) {
        return "Employee " + employeeId + " status: " + hrService.getEmployeeStatus(employeeId);
    }
}

Now we can implement the HRService component as below:

现在我们可以实现HRService组件,如下所示。

@Component
public class HRService {

    public String getEmployeeStatus(Integer employeeId) {
        return "Inactive";
    }
}

Furthermore, we’ll create a mock implementation for the HRService class by using Mockito. We’ll inject this mock into the EmployeeService instance, and we’ll use it in our unit test:

此外,我们将通过使用Mockito来为HRService类创建一个模拟实现。我们将把这个模拟注入EmployeeService实例中,并在单元测试中使用它。

HRService hrService = mock(HRService.class);
when(hrService.getEmployeeStatus(employee.getId())).thenReturn("Active");

Because hrService is a private field without a public setter, we’ll use the ReflectionTestUtils.setField method to inject the mock we created above into this private field:

因为hrService是一个没有公共setter的私有字段,我们将使用ReflectionTestUtils.setField方法将我们上面创建的mock注入到这个私有字段。

EmployeeService employeeService = new EmployeeService();
ReflectionTestUtils.setField(employeeService, "hrService", hrService);

Finally, our unit test will look similar to this:

最后,我们的单元测试将看起来类似于这样。

@Test
public void whenInjectingMockOfDependency_thenReflectionTestUtilsSetField() {
    Employee employee = new Employee();
    ReflectionTestUtils.setField(employee, "id", 1);
    employee.setName("Smith, John");

    HRService hrService = mock(HRService.class);
    when(hrService.getEmployeeStatus(employee.getId())).thenReturn("Active");
    EmployeeService employeeService = new EmployeeService();

    // Inject mock into the private field
    ReflectionTestUtils.setField(employeeService, "hrService", hrService);  

    assertEquals(
      "Employee " + employee.getId() + " status: Active", 
      employeeService.findEmployeeStatus(employee.getId()));
}

We should note that this technique is a workaround for the fact that we’re using field injection in our bean class. If we switched to constructor injection, then this approach wouldn’t be necessary.

我们应该注意到,这种技术是针对我们在Bean类中使用字段注入这一事实的一种变通方法。如果我们改用constructor injection,那么这种方法就没有必要了。

6. Conclusion

6.结论

In this article, we demonstrated how to use ReflectionTestUtils in unit testing by going through several examples.

在这篇文章中,我们通过几个例子来演示如何在单元测试中使用ReflectionTestUtils

Code samples, as always, can be found over on Github.

像往常一样,可以在Github上找到代码样本