知識庫 / Spring RSS 訂閱

反射測試實用指南

Spring,Testing
HongKong
4
01:36 PM · Dec 06 ,2025

1. 引言

`ReflectionTestUtils 是 Spring Test Context 框架的一部分。它是一個用於反射實用方法集合,用於在單元測試和集成測試場景中設置非公共字段、調用非公共方法以及注入依賴項。

在本教程中,我們將學習如何在單元測試中使用 <em>ReflectionTestUtils</em>&nbsp;,通過幾個示例進行講解。

2. Maven 依賴

我們將首先添加所有必要的依賴項的最新版本到我們的 <em pom.xml</em> 中:

<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>

最新版本的 spring-contextspring-test 依賴包可從 Maven 中央倉庫下載。

3. 使用 ReflectionTestUtils 設置非公共字段的值

假設我們需要使用一個具有私有字段的類實例,而該類中沒有公共設置方法,用於單元測試。

我們將首先創建一個該實例:

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

    // standard getters/setters
}

正常情況下,由於沒有為該字段提供公共設置方法,因此我們無法訪問私有字段 id 來為其賦值進行測試。

因此,我們將使用 ReflectionTestUtils.setField 方法為私有成員 id 賦值:

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

4. 使用 ReflectionTestUtils 調用非公共方法

現在假設我們在 Employee 類中有一個私有方法 employeeToString

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

我們可以在不影響 employeeToString 方法的外部訪問的情況下,編寫一個單元測試,如下所示,儘管該方法不直接從外部訪問:

我們可以在不影響 `employeeToString` 方法的外部訪問的情況下,編寫一個單元測試,如下所示,儘管該方法不直接從外部訪問:

@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. 使用 ReflectionTestUtils 注入依賴

假設我們要為以下帶有 @Autowired 註解的私有字段編寫單元測試的 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 類的模擬實現。我們將將此模擬注入到 EmployeeService 實例中,並在我們的單元測試中使用它:

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

因為 hrService 是一個私有字段且沒有公共 setter,我們將使用 ReflectionTestUtils.setField 方法將我們上面創建的 mock 注入到這個私有字段中:

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

最後,我們的單元測試將類似於以下內容:

@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()));
}

我們應該注意的是,這種技術是一種規避方案,因為我們正在使用 Bean 類中的字段注入。如果我們改為使用構造函數注入,那麼這種方法就不再必要了。

6. 結論

在本文中,我們演示瞭如何使用 ReflectionTestUtils 在單元測試中進行操作,通過一系列示例進行説明。

user avatar
0 位用戶收藏了這個故事!
收藏

發佈 評論

Some HTML is okay.