Entreprise Application Integration testing avec CitrusFramework

De nombreux systèmes nécessitent l’intégration de données à partir de formats et protocoles hétéroclites. Citrus Framework s’inscrit dans la démarche de validation en proposant la mise en place de tests d’intégration automatisables par la mise à disposition d’outils de simulation et de mocks permettant la réalisation de tests de bout en bout.Bien que sommaire, la couverture protocolaire au lancement du projet répond aux principaux besoins des projets nécessitant la consommation ou l’exposition de services (notamment pour les architectures SOA).

xtest-scenario.png.pagespeed.ic.fD1y03KPm2

Depuis, le périmètre ne cesse de s’étendre à chaque nouvelle release du framework. A ce jour (version 2.6), l’outil supporte les échanges JMS, HTTP Rest, WebSockets, SOAP, FTP, Message Channel, Fichiers, Mail, SSH. Il propose également, une intégration de Camel pour étendre les capacités des outils de simulation, un interfaçage avec les bus d’évènements Vert.x ou une plateforme de tests Arquillian, ainsi que le contrôle du workflow des conteneurs Docker.

En guise de nouveauté liée à la version 2.6, Citrus s’offre une intégration avec le Behaviour Driven Development en se reposant sur le célèbre framework Cucumber.

 

Le projet

Avec la réalisation de nombreux projets mettant en oeuvre de l’EAI, le groupe Consol* Consulting & Software Solution souhaite trouver un outil de testing répondant aux besoins suivants :

  • La rédaction de tests de bout en bout, pour des solutions nécessitant la mise en place d’EAI;
  • De simuler des systèmes connectés en tant que client ou fournisseur d’informations;
  • De constituer des tests-cases humainement lisibles et détachés de toute complexité d’implémentation des moyens d’échange;
  • De rendre ses tests aisément automatisables pour être joués sur une plateforme d’intégration continue.

Cependant, leurs recherches ne leur permettent pas d’aboutir à une solution répondant à la totalité de leurs critères. C’est ainsi que Citrus Framework voit le jour en 2006, initié par une équipe de 7 personnes du groupe.

Depuis ce jour, le projet reste actif avec une fréquence de mise à jour oscillant entre 5 à 10 commits par semaines et 3 à 4 versions par an.

 

Pour quels besoins?

Citrus Framework s’intègre parfaitement pour la validation des échanges dans une architecture SOA, notamment pour les tests des bus d’échanges ou d’autres composants. Cependant, il convient également parfaitemement à toute solution nécessitant des échanges JMS, HTTP Rest, Ws SOAP, FTP, SSH… On peut donc l’utiliser pour valider le backend d’une application.

Features

Citrus Framework met à disposition les outils suivants pour la conception et l’exécution de cas de tests :

  • Contrôle du flux d’exécution : Ordonnancement des flux entrants / sortants. Définition de stratégies d’expiration et de gestion d’erreur. Intégration de logique avancée (boucles, délais…);
  • Validation des messages : Validation de tout ou partie des headers / payloads (structure et contenu);
  • Accès base : Validation par interaction avec une base de données (génération de jeux de données, tests de cohérence);
  • Exploitation : Intégration avec JUnit/TestNG depuis un IDE, Maven ou ANT. Tests parallèlisables. Reporting. Couverture de test;
  • Accessibilité du langage : Lecture simple et accessible à un non initié. Séparation des scénarios et des configurations;
  • Extensibilité : Créations de fonctions, adaptateurs. Intégration de script groovy.

 

Tests white-box / black-box

De part ses fonctionnalités, l’outil est de nature à répondre à une stratégie de tests orientée black-box (ne nécessitant pas de connaissance sur le fonctionnement interne du système). Ce qui permet la réalisation de tests systèmes et/ou tests d’acceptance dans lequel la solution est soumise à des tests de bout en bout en vue de valider les exigences fonctionnelles et techniques.

Ces tests étant généralement menés par des acteurs n’ayant pas connaissance de l’implémentation (et parfois même, dénués de toute compétence d’écriture logicielle), ils doivent être rédigeables et lisibles humainement. Ce sont des tests d’un haut niveau d’abstraction.

L’outil ne s’attache pas à concevoir des tests white-box (intrusifs), cependant, il peut être utilisé pour la réalisation de tests gray-box (dans lesquels, une connaissance minimale de la plateforme est nécessaire), lesquels s’appuient généralement sur les diagrammes d’architecture et les diagrammes d’état. En effet, l’outil met à disposition des connecteurs jdbc ou des outils de mock permettant une validation intermédiaire dans un flux.

 

Cas concret : application aux micro-services

La présentation suivante a été réalisée à la conférence Devoxx Belgique en 2015. Elle traite de l’utilisation de Citrus Framework pour le test de MicroServices.

Suivre le lien : Testing MicroServices with Citrus “Tools in action” session @Devoxx BE 2015

 

Et en détail?

Citrus propose de multiples intégrations pour l’exécution de ses tests :

  • Depuis votre IDE préféré : Eclipse, IntelliJ IDEA, NetBeans (Citrus s’appuie, au choix, sur deux frameworks de tests ultra-connus : TestNG et JUnit);
  • Depuis ANT;
  • Depuis Maven.

Pour notre exemple, notre choix se tournera vers Maven afin d’inclure l’exécution de nos tests dans le cycle de vie logiciel.

Le framework impose les prérequis suivants :

  • Java >= 1.7
  • Maven >= 3.0

 

Dépendances Maven

L’ensemble des binaires est uploadé à fréquence régulière sur les repositories publiques de Maven. Cependant il peut y avoir un court laps de temps entre la release et le dépot. Je vous suggère donc d’utiliser le repository mis à disposition par consol* labs :

Citrus propose un découpage modulaire par interface (http://mvnrepository.com/artifact/com.consol.citrus). Le principe est simple, le projet minimum ne demande qu’une seule dépendance : le core. Vous ajoutez ensuite les dépendances nécessaires à chacune de vos interfaces.

Il existe également quelques archétypes de base pour mettre en œuvre rapidement son premier projet.

 

Plugin Maven

Citrus framework propose également un plugin permettant de générer des cas de tests ainsi que du reporting d’exécution.

 

Structure du projet

Après génération de l’archetype de base, la structure obtenue est la suivante :

002directories

Dans les versions antérieures à la version 2.4, les tests-case étaient disposés dans un répertoire citrus indépendant. Depuis, il ont été replacés dans le répertoire de ressources.

Les fichiers sont repartis de la façon suivante :

  • test/java/<package> – Emplacement des cas de tests générés et tests en JAVA DSL;
  • test/resources – Emplacement des fichiers de configuration;
  • test/resources/<package> – Emplacement des cas de tests au format XML.

 

Écriture de cas de tests

Les tests déclarés reposent sur l’utilisation du framework de test choisi au moment de la génération du test :

L’outil permet l’écriture de cas de tests au format XML, mais propose aussi un DSL JAVA.

Si votre choix se porte sur la déclaration d’un test case au format XML, une méthode est générée par le plugin, et une annotation référence le cas de test (par son nom) (cf. code ci-dessus). Le fichier XML comprenant le scénario est intégralement basé sur spring. Le fichier de contexte associé intègre des namespaces spécifiques contenant la définition des éléments pour l’écriture de vos scénarios.

Vous avez la possibilité d’inclure plusieurs cas de tests dans une même classe de test.

Comme vous pouvez le constater, la lecture d’un cas de test est aisée.
Il est composé de 3 parties (extensibles par des actions antérieures et postérieures) :

  • La description du test au travers de méta-données;
  • La définition de variables de tests;
  • La définition d’un ensemble d’actions séquencées.

Si vous êtes plutôt JAVA DSL, voici deux courts exemples :

 

Configuration

Comme vous avez pu le constater, le framework repose intégralement sur spring, il n’est donc pas étonnant de retrouver des <beans> dans les fichiers de configuration.

La configuration ci-dessus est le minimum requis pour permettre l’exécution d’un cas de test (accompagné du fichier de propriétés cité)

L’utilisation de Spring apporte un avantage indéniable quant aux capacités d’extension du système. Cela vous laisse également libre de modifier à souhait la structure du fichier de configuration et ainsi apporter de la modularité.

 

Tests actions

Rentrons maintenant dans le détail d’un test case avec les tests actions. Comme évoqué précédemment, chaque test est constitué d’une suite d’actions séquentielles permettant la production ou la réception de message, mais également diverses autres instructions telles que des instructions de boucles, mises en attente, traces, exécutions de scripts SQL et groovy.

Il est également possible de contrôler le flux d’exécution en parallélisant certaines actions par simple ajout de la balise <parallel>. Chaque élément fils est soumis à une exécution parallélisée. A l’inverse, l’élément <sequential>permet de ré-ordonner les actions. D’autres balises sont également disponibles pour conditionner des actions, réaliser des itérations ou mettre le système en attente.

 

Production de message

L’action <send> permet de produire un message à destination du système testé.

Cet élément est constitué d’un corps et d’un ensemble d’entêtes. Il s’attache à décrire le message sans intégrer la complexité de l’implémentation liée au type d’interfaçage. Vous avez la possibilité de changer de mode de transport en minimisant l’impact sur la définition du test.

 

Réception et validation de message

La balise <receive> joue deux rôles. Elle permet de réceptionner un message et de procéder à sa validation.

L’élément <receive> reste en attente le temps de réception d’un message sur le endpoint référencé. Le temps d’attente est paramétrable. A la réception du message, il procède à la validation de son contenu (ou d’une partie de son contenu : corps ou entête). Il est possible de définir à souhait les validations effectuées : Structure (XSD), Données, Segment de données (XPATH , JsonPath) ou spécifier soi-même la méthode de validation par scripting groovy, ou par implémentation de sa stratégie de validation en JAVA.

 

Exemple de validation XPATH

Dans cet exemple, l’utilisation de l’instruction <xpath> permet de cibler la validation sur des éléments spécifiques du message tout en ignorant les autres données.

 

Variables de tests

Citrus permet de définir des variables de test à différentes portées (globales à tous les tests ou locales à un test). Ces variables peuvent être utilisées dans les données lors de la production ou de la réception de messages. Le contenu de la variable est accessible par des placeholders ${…}.

 

Les Fonctions

De la même façon, citrus met également à disposition une liste importante de fonctions (également extensible) pour manipuler des chaines de caractères, encoder, décoder, générer des identifiants uniques, générer des dates mais aussi des fonctions de type matcher, permettant de valider une partie du contenu du message selon des règles précises ou génériques.

Tout comme les variables, ces fonctions sont utilisables directement dans les messages des actions de production et réception.

 

Exemple de génération de date dans un fichier de resource externalisé – en production de message

 

Exemple de fonction de validation dans une réception de message

 

Les endpoints

Comme observé précédemment, chaque production ou réception de message est liée à un endpoint permettant de spécifier l’implémentation de transmission du message.

Citrus propose des composants prêts à l’emploi, tels que des clients HTTP, serveurs HTTP… Chacun d’eux peut être utilisé pour définir des endpoints ciblant ainsi un service en production ou en consommation.

 

Exemple de endpoint en consommation de message

Si on reprend l’exemple ci-dessus, il peut être utilisé à la fois pour la production d’un message HTTP dans une action de type <send> mais également pour valider la réception de la réponse associée dans une action secondaire de type <receive> (validation du code retour et/ou des données associées).

Dans les deux cas, le endpoint est référencé dans les deux actions par l’ajout d’un attribut dans la balise.

 

Les Acteurs

La notion d’acteur sur citrus est différente de celle présentée dans la programmation orientée acteur (prenez par exemple Akka). Dans notre cas, elle ne s’attache qu’à apporter une représentation d’un partenaire du système (application cliente par exemple) et ainsi grouper les endpoints.

Cette information est notamment utile pour les tests de disponibilité/indisponibilité d’un service. En utilisant l’acteur, il est possible de momentanément inhiber tous les endpoints associés et ainsi simuler l’indisponibilité d’un partenaire.

 

Les Bouchons

Maintenant que vous avez compris le fonctionnement, le bouchon va vous parraitre d’une évidence… On y retrouve les ingrédients communs : un endpoint, une action de type <send> et une de type <receive>.

Qu’est-ce qui change? En premier lieu, le endpoint ne s’appuie pas sur un composant client mais sur un composant serveur (exemple pris d’un composant HTTP), les actions <receive> et <send> sont à ordonner dans le sens inverse (on définit l’instruction permettant l’attente et la validation du message avant de procéder à l’envoi de la réponse).

 

Configuration du endpoint serveur

 

Attente du message et production de la réponse

 

Les templates

Un autre outil qui vous sera nécessaire pour factoriser vos actions dans les cas de tests : les templates. Le principe est simple. Vous déclarez un snippet d’actions pouvant prendre des paramètres en entrée, puis vous l’appelez dans vos cas de tests.

 

Déclaration d’un template

 

Utilisation d’un template

 

Exemple de cas de test

Le cas de test ci-dessous met en avant l’ordonnancement des actions dans le cadre d’un échange :

Client -> Système testé -> Mock Server -> Système testé -> Client

 

Le Reporting

De part son architecture, l’outil offre des traces d’exécution détaillées et des rapports standardisés.

 

Les logs d’exécution

Les traces d’exécution sont très verbeuses, et permettent d’analyser l’ensemble des échanges à des fins de debug. Le niveau de logs est maintenable par une simple configuration log4j.

 

Les rapports d’exécution

En fin de test, deux fichiers de reporting sont générés : un au format JUnit exploitable dans tout outil de build et un fichier HTML.

003tests_results

Il est tout à fait possible d’utiliser des outils additionnels pour permettre d’apporter d’avantages de lisibilité aux rapports.

Allure, par exemple (voir http://allure.qatools.ru), est un outil qui se base sur les rapports générés par les frameworks de tests pour générer de nouveaux rapports étendus par l’interprétation d’annotations de tests. Il peut être utilisé pour ajouter une dimension comportementale aux rapports par l’ajout d’identification des features et stories auxquels les tests se rapportent. Essentiel pour le BDD!

bdd

 

La documentation de test

Pour finir, le plugin permet de générer une documentation de description des tests du projet (au format EXCEL ou HTML).

Retour d’expérience

Après quelques mois d’utilisation pour nos tests systèmes sur des applications de médiation, il en ressort les éléments suivants :

  • L’outil apporte une grande souplesse dans les actions, de part ses multiples choix d’interfaces, ainsi que dans ses capacités d’extension;
  • Il est possible de constituer ses propres actions pour les intégrer dans des cas de test;
  • L’écriture des tests est aisée, et basée sur des actions simples et à la portée de tous (émission et réception de flux);
  • Le framework permet la construction de tests autoportants. Aucun module additionnel n’est pas ajouter contrairement à d’autres outils nécessitant la mise en place de bouchons indépendants. Citrus fournit à la fois les éléments clients et serveurs permettant d’interagir avec le système sous test;
  • L’intégration dans le cycle de vie maven permet de facilement faire porter l’exécution des tests par une plateforme d’intégration continue;
  • Le produit se base sur des frameworks connus et reconnus, Spring, Log4j, JUnit, TestNG, offrant une grande souplesse, une facilité de maintenance et d’exploitation, une bonne évolutivité et un bon support;
  • La documentation en ligne est très complète et précise. Et l’ouverture du code permet en cas de nécessité absolue de permettre un debug avancé;
  • Il est possible d’externaliser les jeux de données dans des fichiers XML (par exemple dans le cas de message XML) en dehors des cas de tests pour permettre leur réutilisation.

Nous avons, par contre, rencontré les difficultés suivantes :

  • L’ordre de réception des messages par les actions <receive> doit être garanti. En effet, si deux messages sont attendus en réception sur un même endpoint, il est important qu’ils soient reçus dans l’ordre définis dans le cas de test. Ceci complique les cas de tests nécessitant de multiples requêtes sur un système ne permettant pas de garantir l’ordre de traitement et de délivrance des messages (et il y en a beaucoup). Pour palier à ce comportement, vous devez utiliser un <selector> dans le <receive> de votre message, afin d’identifier le message que vous attendez;
  • A l’encontre de la documentation utile à la rédaction de test-cases, la documentation d’utilisation des composants est peu fournie. La configuration nécessite souvent de mettre son nez dans le code de citrus pour comprendre le fonctionnement. Il existe aussi très peu d’exemples;
  • Il est nécessaire d’avoir un certain niveau dans l’utilisation de spring pour manipuler les configurations (si aucun exemple n’existe sur votre projet);
  • Il faut également bien distinguer les différentes variables disponibles (variables de tests ayant une application dans les cas de tests et variables issues de properties-placeholder spring pour les configurations des composants). Par exemple, il n’est pas forcément évident d’externaliser la déclaration des variables de tests dans des fichiers.

Alternatives

Depuis le début du projet, nous avons travaillé avec plusieurs autres frameworks de tests d’intégration, dont Citrus, objet de cet article mais aussi avec 2 autres outils : RobotFramework et un framework construit de toute pièce. Mais dans l’ensemble, Citrus s’impose comme la solution clé en main la plus complète et évolutive.

En comparaison avec RobotFramework, on constate que ce dernier :

  • Impose des contraintes de portabilités (il repose sur python d’une part, et des problèmes de compabilités des tests peuvent être rencontrés dans le cas d’utilisation de versions différentes du langage);
  • Utilise une syntaxe soumise à de nombreuses erreurs (attention à ne pas oublier les doubles espaces!!);
  • Ne sait pas constituer des composants de mock, il est alors nécessaire de constituer ses propres bouchons dans le langage de son choix.

 

En conclusion, Citrus Framework est une vrai bonne surprise qui nous a vraiment permis de mettre en place une réelle stratégie de tests d’intégration automatisables en combinant la simplicité d’utilisation aux capacités intrinsèquement fournis par le socle spring. Je vous le recommande donc chaudement.

 

Leave a Reply

Your email address will not be published. Required fields are marked *