Spring Boot Testing - Using ReflectionTestUtils for Unit Testing
Table of Contents
Notes from Spring documentation:
ReflectionTestUtils
is a collection of reflection-based utility methods for use in unit and integration testing scenarios.
There are often times when it would be beneficial to be able to set a non-public field, invoke a non-public setter method, or invoke a non-public configuration or lifecycle callback method when testing code involving, for example:
- ORM frameworks such as JPA and Hibernate which condone the usage of private or protected field access as opposed to public setter methods for properties in a domain entity.
- Spring’s support for annotations such as
@Autowired
,@Inject
, and@Resource
which provides dependency injection for private or protected fields, setter methods, and configuration methods. - Use of annotations such as
@PostConstruct
and@PreDestroy
for lifecycle callback methods.
Set a Value of a Non-Public Field
@Test
public void whenNonPublicField_thenReflectionTestUtilsSetField() {
// Employee class has a private field called "id"
Employee employee = new Employee();
ReflectionTestUtils.setField(employee, "id", 1);
assertTrue(employee.getId().equals(1));
}
Invoke a Non-Public Method
Private method in the class that needs to be tested
private String employeeToString(){
return "id: " + getId() + "; name: " + getName();
}
Testing it
@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"));
}
Inject Dependencies
Class with a private injected dependency
@Component
public class EmployeeService {
@Autowired
private HRService hrService;
public String findEmployeeStatus(Integer employeeId) {
return "Employee " + employeeId + " status: " + hrService.getEmployeeStatus(employeeId);
}
}
Dependency that is being injected
@Component
public class HRService {
public String getEmployeeStatus(Integer employeeId) {
return "Inactive";
}
}
Mocking the injected class in the test class
HRService hrService = mock(HRService.class);
when(hrService.getEmployeeStatus(employee.getId())).thenReturn("Active");
Testing it
EmployeeService employeeService = new EmployeeService();
ReflectionTestUtils.setField(employeeService, "hrService", hrService);
Injecting “Value"s
public class MyClass {
@Value("${value.from.property.file}")
private String valueFromProperyFile;
public void myMethod() {
// change the value of valueFromProperyFile in this method
}
}
Setting a dummy value to it before each test
public class MyClassTests {
@Spy
@InjectMocks
MyClass myClass;
@BeforeEach
void initialize() {
ReflectionTestUtils.setField(myClass, "valueFromProperyFile", "dummyValue")
}
Testing the value of a non-public variable
@Test
public void myTestMethod() {
assertDoesNotThrow -> (() -> myClass.myMethod());
verify(myClass, times(1)).myMethod();
assertEquals("changedValue", ReflectionTestUtils.getField(myClass, "valueFromProperyFile"));
}