Перейти к основному содержимому

Руководство по ReflectionTestUtils для модульного тестирования

· 3 мин. чтения

1. Введение

ReflectionTestUtils является частью среды Spring Test Context. Это коллекция служебных методов на основе отражения, используемых в модуле, и сценариев интеграционного тестирования для установки непубличных полей, вызова непубличных методов и внедрения зависимостей.

В этом руководстве мы рассмотрим, как мы можем использовать 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>

Последние зависимости spring-context , spring-test можно загрузить из репозитория Maven Central.

3. Использование ReflectionTestUtils для установки значения непубличного поля

Предположим, нам нужно использовать в нашем модульном тесте экземпляр класса с закрытым полем без общедоступного метода установки.

Начнем с его создания:

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

// standard getters/setters
}

Обычно мы не можем получить доступ к идентификатору частного поля , чтобы присвоить значение для тестирования, потому что для него нет общедоступного метода установки.

Затем мы можем использовать метод ReflectionTestUtils.setField , чтобы присвоить значение частному идентификатору члена :

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

assertTrue(employee.getId().equals(1));
}

4. Использование ReflectionTestUtils для вызова закрытого метода

Теперь представим, что у нас есть приватный метод employeeToString в классе Employee :

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");

assertTrue(ReflectionTestUtils.invokeMethod(employee, "employeeToString")
.equals("id: 1; name: Smith, John"));
}

5. Использование ReflectionTestUtils для внедрения зависимостей

Допустим, вы хотите написать модульный тест для следующего компонента Spring, имеющего приватное поле с аннотацией @Autowired :

@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";
}
}

Кроме того, давайте создадим фиктивную реализацию класса HRService с помощью Mockito . Мы добавим этот макет в экземпляр EmployeeService и будем использовать его в нашем модульном тесте:

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

Поскольку hrService является приватным полем без общедоступного установщика, мы будем использовать метод ReflectionTestUtils.setField , чтобы внедрить созданный выше макет в это приватное поле.

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

Мы должны отметить, что этот метод является обходным путем для того факта, что мы используем внедрение полей в нашем классе компонента. Если бы мы переключились на внедрение конструктора, то этот подход был бы не нужен.

6. Заключение

В этом руководстве мы показали, как использовать ReflectionTestUtils в модульном тестировании, рассмотрев несколько примеров.

Образцы кода, как всегда, можно найти на Github .