Willing BONOU-SELEGBE

Software Engineer

Willing BONOU-SELEGBE

Software Engineer

Download my resume

Spring-Boot: Ecrire des tests unitaires & d’integration

willing - 19/03/2020

Tests d’intégration Controler-Service-Repository-Database

Le code du test d’intégration des couches controler-service-repo-db est la suivante:

Ce code ressemble à s’y méprendre à celui du test unitaire de notre Controler. Mais attention, le Diable se cache dans les détails! Ce test est fondamentalement différent de notre test unitaire pour les raisons suivantes:

  • D’abord, notez la présence de l’annotation @SpringBootTest qui vient remplacer l’annotation @WebMvcTest utilisée dans le TU. Ces deux annotations sont différents. @SpringBootTest va lancé le contexte d’application complet. Autrement dit, Spring va configurer un webapplicationcontext avec tous les classes annotés par @Controller, @Service, @Repository, @Configuration, @Bean etc. Cet anotation indique également à SpringBoot de charger les plugins nécessaires dans le pom.xml et de charger le fichier applications.properties. Bref, l’annotation @SpringBootTest va nous permettre de lancer notre application Spring Boot complet comme en environnement de prod, et c’est justement ce que nous souhaitons faire dans un test d’intégration!
  • Une autre différence majeure se situe à la ligne 11 de notre test. Notez que l’annotation @MockBean du TU s’est transformée en @Autowired. En effet, cette fois-ci nous ne voulons plus mocker la couche service mais nous voulons injecter l’instance réelle de notre Bean EmployeeService. Ce qui nous permettra d’avoir accès à la véritable instance de notre repository et de notre base de donnée. Ce seul test d’intégration nous permet donc de tester à la fois le Controller, le service, le repository et la base de donnée.
    Nous aurons également pu tester le Client dans ce même test, mais pour des raisons pratiques nous l’isolerons dans un test d’intégration distinct.

Vous aurez également remarqué que notre test d’intégration hérite de la classe AbstractEmployeeControllerITTest. Cette classe contient toute la logique de lancement et de configuration de la base de données. Regardons de plus près le code de cette classe:

Plusieurs explications s’imposent sur ce code:

  • Les lignes 10 à 18 permettent de configurer et de démarrer un PostgreSQLContainer. PostgreSQLContainer est un type particulier de TestContainer. Il faudrait un article complet pour expliquer les TestContainers, mais retenez que ce sont des types de la library Junit qui nous permettent de démarrer un container docker afin d’exécuter des tests d’intégration. En l’occurence ici ils nous permettent de lancer une base de donnée PostgreSQL comme celle que nous lancerons en production, de sorte que nos tests d’intégration soient le plus réaliste possible.
  • La classe interne static class Initializer permet d’injecter dynamiquement des propriétés additionnelles dans le fichier application.properties. En effet, notre container PostgreSQL va essayer de se lancer sur un port libre aléatoirement. Nous ne pouvons pas savoir à l’avance sur quelle port il se lancera. Pour cette raison nous configurons dynamiquement (c’est-à-dire directement dans le code java et non dans le fichier application.properties) toutes les properties qui dépendent du port.
  • L’annotation @Sql(« classpath:add_employees.sql ») indique à Spring d’exécuter toutes les requettes présentes dans le fichier add_employees.sql. Ce fichiers contient quelques INSERT qui nous permettent d’alimenter notre base de donnée de test une fois le container PostgreSQL démarré.
  • Les annotations @Transactional et @Rollback permettent respectivement de démarrer une transaction hibernate avant toutes les méthodes de test, et d’arrêter ces transactions après l’execution (ou l’échec) de toutes les methodes de la classe de test.
  • L’annotation @TestPropertySource indique à Spring l’emplacement du fichier des properties de l’application. Ce fichier contient les informations permettant à hibernate et flyway de se connecter à la base de donnée.
  • Un élément peut visible dans ce bout de code est l’utilisation de la library flyway. Cette library permet de versionner les migrations de la base de donnée d’une application. Dans notre cas, nous l’utilisons pour créer la table Employee grâce au fichier V1__Create_Employee_table.sql. Sans celà, l’annotation @Sql(« classpath:add_employees.sql ») échouerait avec un joli message du type « Cannot insert into table Employee, table not exists … »

Remarques:

  • Nous aurons pu mettre tout le code de la classe AbstractEmployeeControllerITTest directement dans la classe EmployeeControllerITTest. Cela n’aurait eu aucun impact de performance dans notre cas, puisque nous n’avons qu’une seule classe de test qui a besoin de docker. Le fait d’externaliser ce code dans une classe abstraite permet de mutualiser un même container docker pour toutes les classes de Test qui en ont besoin.
  • Nous aurons également pu utiliser l’annotation @DataJpaTest qui permet de lancer une base de données en mémoire H2 pour nos tests. Cette approche est plus légère et plus rapide. Néanmoins je déconseille fortement cette technique. En procédant de la sorte vous pourrez avoir des tests d’intégration qui réussissent alors que la requête échouera en prod. Cela arrive par exemple quand vous utilisez des requêtes spécifiques pour PostgreSQL. H2 interprête souvent mal les requêtes spécifiques. L’approche H2 n’est donc a utilisée que si vous n’avez pas la possibilité de lancer un container docker sur votre CI, ou si vous avez d’autres tests (fonctionelles, e2e) qui valident les requêtes en prod. Dans tous les autres cas de figures, privilégiez des tests d’intégration avec un container docker ou une instance réelle de votre base de données.

A présent, nous avons presque couvert toutes les briques de notre application. Mais il reste une dernière faille: Nous ne savons pas si notre client respecte le contrat d’intégration avec le service distant. Rien ne nous garanti que notre Client est bien configuré pour appeler le service remote, ou que l’url du service remote est la bonne, ou encore que le type de retour du service remote est conforme à celui attendu.
Dans la page suivante de notre article, nous verrons comment palier à ces lacunes en écrivant un test d’intégration pour notre Client.

Pages : 1 2 3 4 5 6 7

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Étiquettes : , , ,

Whatever the mind of man can conceive and believe, it can achieve

Napoleon Hill