Guide de ReflectionTestUtils pour les tests unitaires

1. Introduction

ReflectionTestUtils fait partie du framework Spring Test Context. Il s'agit d'une collection de méthodes utilitaires basées sur la réflexion utilisées dans une unité et de scénarios de test d'intégration pour définir les champs non publics, appeler des méthodes non publiques et injecter des dépendances.

Dans ce didacticiel, nous verrons comment nous pouvons utiliser ReflectionTestUtils dans les tests unitaires en passant par plusieurs exemples.

2. Dépendances de Maven

Commençons par ajouter les dernières versions de toutes les dépendances nécessaires à nos exemples dans notre pom.xml :

 org.springframework spring-context 5.1.2.RELEASE   org.springframework spring-test 5.1.2.RELEASE test 

Les dernières dépendances spring-context, spring-test peuvent être téléchargées à partir du référentiel Maven Central.

3. Utilisation de ReflectionTestUtils pour définir une valeur d'un champ non public

Supposons que nous ayons besoin d'utiliser une instance d'une classe ayant un champ privé sans méthode de définition publique dans notre test unitaire.

Commençons par le créer:

public class Employee { private Integer id; private String name; // standard getters/setters }

Normalement, nous ne pouvons pas accéder à l' ID de champ privé pour attribuer une valeur pour le test, car il n'y a pas de méthode de définition publique pour cela.

Nous pouvons ensuite utiliser la méthode ReflectionTestUtils.setField pour attribuer une valeur à l' ID de membre privé :

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

4. Utilisation de ReflectionTestUtils pour appeler une méthode non publique

Imaginons maintenant que nous ayons une méthode privée employeeToString dans la classe Employee :

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

Nous pouvons écrire un test unitaire pour la méthode employeeToString comme ci-dessous, même si elle n'a aucun accès depuis l'extérieur d'une classe 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. Utilisation de ReflectionTestUtils pour injecter des dépendances

Disons que vous souhaitez écrire un test unitaire pour le composant Spring suivant ayant un champ privé avec l' annotation @Autowired :

@Component public class EmployeeService { @Autowired private HRService hrService; public String findEmployeeStatus(Integer employeeId) { return "Employee " + employeeId + " status: " + hrService.getEmployeeStatus(employeeId); } }

Nous pouvons maintenant implémenter le composant HRService comme ci-dessous:

@Component public class HRService { public String getEmployeeStatus(Integer employeeId) { return "Inactive"; } }

De plus, créons une implémentation fictive pour la classe HRService en utilisant Mockito. Nous injecterons cette maquette dans l' instance EmployeeService , et nous l'utiliserons dans notre test unitaire:

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

Étant donné que hrService est un champ privé sans setter public, nous utiliserons la méthode ReflectionTestUtils.setField pour injecter la simulation que nous avons créée ci-dessus dans ce champ privé.

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

Enfin, notre test unitaire ressemblera à ceci:

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

Nous devons noter que cette technique est une solution de contournement pour le fait que nous utilisons l'injection de champ dans notre classe de bean. Si nous passions à l'injection de constructeur, cette approche ne serait pas nécessaire.

6. Conclusion

Dans ce didacticiel, nous avons montré comment utiliser ReflectionTestUtils dans les tests unitaires en passant par plusieurs exemples.

Des exemples de code, comme toujours, peuvent être trouvés sur Github.