Injection De Token Oauth2 Dans Des Tests JUnit
Lorsque l’on développe des tests API, il arrive fréquemment de devoir s’authentifier pour effectuer l’appel. Réécrire le code permettant de récupérer un token valide est non seulement fastidieux, source d’erreur, mais également une mauvaise pratique en termes de maintenabilité. Nous allons voir comment injecter un token valide directement en paramètre de test JUnit de manière à pouvoir l’utiliser simplement là où cela est nécessaire.
Introduction
Une solution couramment mise en place pour répondre à cette problématique est de créer une méthode utilitaire permettant de récupérer le token suivant le flow de connexion supporté par l’application. Cette méthode sera probablement définie dans une classe utils ou common, noyée au milieu de nombreuses autres méthodes. Cette classe étant elle-même sûrement dans un package util totalement générique. Les problématiques sont très bien expliqué dans cet article. Je vous propose donc de voir comment mettre en œuvre le mécanisme d’extension JUnit pour réaliser proprement une injection de token dans chaque test, le nécessitant.
JUnit Extension
Les extensions JUnit est une fonctionnalité apparue avec JUnit 5 permettant de modifier le comportant de JUnit lors de l’exécution des tests. La documentation est très bien faite, je vous invite à la lire. Une extension s’utilise via une annotation spéciale @ExtendWith sur une classe de test, un champ de classe, un paramètre de constructeur, dans une méthode de tests ou sur les méthodes annotées @BeforeAll, @AfterAll, @BeforeEach, and @AfterEach.
Exemple pour une classe :
Ou un test en particulier
BearerParameterExtension
Il existe plusieurs types d’extensions proposées par JUnit. Nous allons utiliser une extension implémentant l’interface ParameterResolver. Cette interface définie une extension permettant d’injecter une valeur lorsque qu’un test demande un paramètre spécifique dans la signature de sa méthode. Dans notre cas, notre test va demander un token en paramètre.
Un objet Token est passé ici à la place d’un simple String afin de permettre à notre extension de différencier notre token de n’importe quel autre paramètre de type String.
Voici maintenant le code de l’extension :
|
|
Plusieurs points à préciser ici :
- Notre extension utilise une annotation à appliquer sur le paramètre du test pour configurer l’injection.
- L’authorization provider utilisé ici est
Keycloak. J’utilise donc le concept derealmpour reconstruire dynamiquement l’URL du token provider dans la méthodeauthenticate. Une implémentation plus générique demanderait l’URL du token endpoint mais je préfère garder l’annotation la plus concise possible. - La méthode
authenticateva faire l’authentification et récupérer le token. C’est l’équivalent de notre méthode utilitaire dont je parlais en début d’article.
Utilisation de l’extension
En reprenant l’exemple du test ci-dessus, nous pouvons utiliser l’extension de la manière suivante :
|
|
Nous pouvons donc maintenant simplement injecter des tokens valides dans nos tests. Ici, nous l’utilisons via restAssured pour effectuer une requête POST sur notre API en étant authentifié.
Point à améliorer
Le code n’est pas parfait. Voici quelques points à améliorer selon moi :
- Prendre en compte, plusieurs flow de connection via différentes annotations (
@ClientCredentials,@DirectGrant,@AuthorizationCode…) - Remplacer la classe Env par un chargement des variables via fichiers de configuration par environnement
- Dans le test, trouver un autre moyen que de passer en dur le secret à l’annotation
Conclusion
Le système d’extension de JUnit permet d’injecter facilement des objets en paramètre des tests. L’injection de dépendance permet de découpler la logique de préparation des données nécessaires à l’exécution du test de celle purement fonctionnelle. Cela apporte une plus grande lisibilité et une maintenance facilitée. D’autres extensions existent avec JUnit pour, par exemple agir sur le cycle de vie des tests et permettre de démarrer automatiquement des conteneurs docker, importer des données dans une base… .
Annexe
Pour référence voici les classes Token et `TokenResponse utiliser dans le code ci-dessus :
|
|