Les tests fonctionnels en .NET
Dans cet article, nous avons décidé de vous parler de tests fonctionnels appliqués à du code .NET.
Vous pourrez (re-)découvrir l'intérêt et le fonctionnement de la mise en place ce type de tests sur votre application.
Il faut savoir qu'à ce jour, il existe des dizaines de frameworks .NET de tests fonctionnels. C'est pourquoi nous avons décidé de nous concentrer sur les trois frameworks qui semblent les plus pertinents aujourd'hui, à savoir :
- Concordion
- Fitnesse
- Specflow
Lors de la rédaction de cet article, nous avons développé en parallèle une petite application, sur laquelle nous avons mis en place les trois frameworks sus-cités. Aussi vous pourrez voir de vous-même les principes évoqués !
La théorie
Pourquoi intégrer des tests fonctionnels dans mon application ?
Ma documentation n’existe pas ou n’est pas maintenue à jour
La documentation est toujours nécessaire (même dans un projet agile). Cependant il s’agit bien souvent de documents Word perdus dans les limbes du système de fichiers de l’entreprise. Cette documentation n’est souvent pas mise à jour. Les tests fonctionnels permettent de fixer un support partagé entre les développeurs et Product owner (PO) ou business analyst (BA). Dans les faits, toutes les personnes intervenant sur le projet utilisent ces tests fonctionnels. Si les douleurs se limitent à de la documentation, il est plus simple de se servir d’un wiki.
La qualité de la communication entre le métier et les développeurs n'est pas bonne
Un outil de tests fonctionnels permet de faciliter le dialogue entre les développeurs et les personnes du métier. En effet, l'outil apporte de la valeur au deux mondes : la spécification par l'exemple met rapidement en exergue les incohérences dans la spécification, aidant les rédacteurs.
Transformer ces exemples en tests exécutables permet aux développeurs d'obtenir un retour rapide et fiable sur le code qui vient d'être produit. Il aide aussi à définir plus clairement le périmètre.
Le temps de recette est trop long
La recette de mon application peut prendre plusieurs semaines et le délai de mise sur le marché de mes évolutions est bien trop grand. Les frameworks de tests fonctionnels permettent de rendre les spécifications exécutables, ce qui permet d’automatiser les tests de recettes et ainsi que les faire exécuter par une usine de développement. Par exemple, à Octo nous avons un projet qui a plus de 7 ans d'ancienneté : sans test fonctionnel, la recette prendrait plusieurs semaines ! Mais grâce à une série de tests fonctionnels automatisés (qui met 45 minutes à exécuter), l’équipe arrive à livrer une version toutes les deux semaines. Ce projet contient 117 724 lignes de code hors tests pour 215 tests fonctionnels comprenant parfois plus d'une centaine assertions.
La mise en production introduit des régressions critiques
Les mises en production introduisent de nombreuse régressions. L’humain fait de nombreuses erreurs, et peut passer à coté de dysfonctionnements rencontrés dans l'application. Pour améliorer la qualité de la recette, il est plus sûr d'avoir aussi des tests de spécification automatiques et fiables qui couvriront les parties les plus critiques de votre système.
Quel est le périmètre de mes tests fonctionnels ?
Il convient de fournir une limite à nos tests fonctionnels. Il est possible de tester absolument tout mais cela risque de coûter très cher (en temps de développement). On préférera tester ce qui n’est pas IHM de notre application.
Par exemple si l'on souhaite tester une application MVVM, on va se brancher sur le View-Model de l’application. Pour une application Web basée sur une API REST, on va chercher à tester l’API REST car la vue est souvent vidée de logique métier.
Si toutefois on désire tester les composants "vue", il est possible d'ajouter des tests de type Sélénium.
Enfin et surtout, on va tester un système (une ou plusieurs applications) qui apporte une valeur métier. On testera un module technique au sein d'un système complet. Par exemple dans le cas d'un système bancaire, on souhaite tester le système de virement. Chaque test couvrira dans son ensemble l'application Web, le module de transformation REST/SOAP et enfin le backend portant la logique métier.
Densité des différents types de tests
En écrivant des tests de spécification, il convient de garder en tête la densité des différents types de test : nos tests fonctionnels viennent en complément de nombreux tests unitaires qui vérifient l'implémentation technique du système.
Comment intégrer les tests fonctionnels dans mes processus ?
Les tests fonctionnels sont des spécifications par l’exemple. Ils doivent être intégrés au plus proche de la spécification, en y ajoutant des cas de tests concrets. Pour chaque cas métier, on fournira des entrées et définira les sorties attendues.
Généralement le PO/BA écrit les US dans le format du framework (Wiki/Gherkin/HTML) en spécifiant les règles de gestions et les tests de spécifications. Les développeurs ont ensuite pour responsabilité de créer la “glue” (souvent appelée fixture) entre les tests et l’application.
Quel langage préférer lors de l’écriture des user stories
Nous n’avons encore jamais vu le métier qui s’alignait avec le langage des informaticiens : c’est à l’IT de s’aligner avec le métier. Les tests fonctionnels ont cet avantage qu'ils utilisent un langage proche du métier.
Il est bon de définir une structure pour faciliter la communication entre le PO/BA et l’équipe technique. Par exemple, le langage Gherkin (décrit plus loin dans cet article) impose un cadre structurant l'écriture d'un test.
Au lieu de dire :
on insère cette ligne en BDD : INSERT "Thibaut", 34 IN accounts ...
un test bien fait aura plutôt cette forme :
SACHANT que l’on a une personne nommée Thibaut avec un compte crédité de 34€ ...
Quel est le coût de mise en place de tests fonctionnels sur mon application ?
Tout va dépendre de votre projet mais le coût de développement peut aller jusqu’à doubler : cela dépend de votre complexité fonctionnelle. Par exemple, si vous êtes responsable de développer des API, vous et vos utilisateurs utilisez le même langage, les API REST. Vos fixtures vont rester simples, et le coût sera faible. Toutefois il se peut que votre métier soit compliqué et que la transformation (par la fixture) des spécifications en tests compréhensible par votre système soit coûteuse.
Cout de correction d'un bug en fonction du moment où il est detecté
Toutefois, il convient de prendre en compte que la recette va être grandement simplifiée et deviendra donc moins coûteuse. Vous allez détecter les bugs bien plus tôt dans le processus de développement, et comme le montre le graphique ci-contre, il coûteront moins cher à corriger.
Si malgré tout, vous ne souhaitez pas investir complètement dans les tests fonctionnels, n'hésitez pas à les mettre en place dans un premier temps sur les fonctionnalités critiques ou compliquées.
Attention à un point : les tests automatisés sont re-jouables à l'infini et attendent toujours le même résultat. Il convient donc de gérer les jeux d'essai des bases de données ou encore de simuler les services externes appelés.
Après la théorie : la pratique
L’application
Nous avons donc développé une application disponible sur github avec toutes les instructions dans le readme pour lancer l’application et exécuter ses tests unitaires et fonctionnels.
Il s’agit d’une simple liste de tâches sobrement nommée TodoYouToo.
L’application comprend deux écrans :
Le premier permet de lister toutes ses tâches : elles sont composées d’un nom, d’une date et d’un marqueur indiquant si la tache a été effectuée.
Il est possible de modifier le marqueur doing/done.
L'icône “plus” permet d'accéder à l’écran d’ajout de tâche.
Le second permet d’ajouter des tâches avec un nom et une échéance.
Les Frameworks de tests fonctionnels
Nous avons choisi de tester notre petite application avec trois frameworks : Concordion, Fitnesse, Specflow. Au fur et à mesure de l’article, nous vous invitons à parcourir les implémentations correspondantes sur le projet disponible sur github. Les principales caractéristiques sont listées juste dans le tableau ci-dessous :
Fitnesse | Specflow | Concordion | |
Type | Serveur Java + add-on .NET | Bibliothèque (.dll) | Bibliothèque (.dll) |
Origine | Fitnesse (java) | Cucumber (ruby) | Concordion (Java) |
Syntaxe | Wiki | Gherkin | Html |
Liberté dans l’écriture | Faible : wiki très contraignant | Moyenne : La syntaxe Gherkin est obligatoire mais peu contraignante | Le style est libre, le html permet une grande flexibilité |
Outil d’écriture des tests | Le navigateur web | Visual studio | Un IDE ou un traitement de texte |
Intégration dans Visual Studio | Aucune | Très bonne | Mauvaise |
Lisibilité | Pas optimale | Possibilité d’exporter sous différent format donc bonne | Très bonne (et même personnalisable) |
Accessibilité aux "Métiers" | Plutôt bonne | Moyenne | Mauvaise |
Licence | Open source | Open source | Open source |
Coût | Gratuit | Gratuit | Gratuit |
Rapide présentation des caractéristique des frameworks de tests fonctionnels
Concordion
[Résultat des tests Concordion de TodoYouToo]
Concordion se veut similaire à Cucumber mais en se concentrant sur la lisibilité et la simplicité. L’outil a été développé pour le monde Java et s’intègre parfaitement avec Junit. Les tests sont rédigés en HTML puis sont liés par des attributs spécifiques avec une fixture qui sert de glue avec l’application.
Comme le montre l’exemple ci-dessus, il est possible de créer du HTML de la manière qui nous arrange le plus, ce qui est très agréable. Cependant de grands pouvoirs impliquent de grandes responsabilités, il est simple de tomber dans certaines dérives, comme des tests trop proches de la mise en oeuvre technique. Il est conseillé de consulter les bonnes pratiques pour éviter de tomber dans certains travers.
Enfin l’outil se révèle très simple, il est composé d’une douzaine de commandes, il est donc simple à prendre en main. La documentation est courte mais tout à fait suffisante.
Il est important de noter que les spécifications sont liées au code. Elles vivent avec ce dernier dans votre gestionnaire de source préféré. Les spécifications sont versionnées avec l’application, il est simple de les faire évoluer sans perdre d’historique. Toutefois, il est préférable que la personne rédigeant les spécifications soit familière avec les outils de gestion de source.
L’outil fonctionne et est apprécié dans le monde Java mais qu’en est-il dans le monde .NET ?
Concordion.NET a été entièrement réécrit en C# en étendant le lanceur Nunit. Malheureusement son intégration n’est pas aussi simple qu’en Java. Voici les différents problèmes que l’on a pu rencontrer lors de l’utilisation de concordion :
- les DLL ne sont pas packagées au format nuget, il est nécessaire de créer un dossier dans lequel mettre les DLL
- Le second problème est le lanceur : il est possible d’utiliser Gallio qui n’est plus maintenu depuis 2 ans. Le second est Nunit : celui-ci fonctionne bien mais il y a quelques problèmes de compatibilité avec le plugin qui a été développé : impossible de lancer les tests dans Visual Studio avec Nunit Adapter ou resharper. il est nécessaire d’utiliser testDriven (qui est payant). La procédure pour le lancement des tests est détaillée dans le readme
L’outil serait mature si ces deux problèmes étaient résolus mais l'un d'entre eux est ouvert sur github depuis 1 an déjà.
Et sur le terrain, qu’est-ce que ça donne ?
L'idéal est d’avoir un rédacteur des spécifications qui est familier avec la technique :
- Le format d’écriture est le HTML, ce qui peut être déroutant pour les personnes étrangères à la technique. Les besoins restent cependant très simples, et il suffit de connaitre les balises <p>, <div>, et <table>. Un simple accompagnement peut être suffisant.
- Les spécifications résident dans le gestionnaire de source. Le rédacteur peut faire des commits, cependant certains SCM peuvent être déroutants. Il est toujours possible d’envoyer les fichiers par mail à un développeur le cas contraire.
Une fois les spécifications écrites, l’équipe se met d’accord pour savoir s’il est nécessaire de rendre le test exécutable. Si oui alors les développeurs écrivent la fixture pour le lier au code.
Exemple de Fixture Concordion
Enfin il est possible de rendre disponible les résultats HTML afin qu’il servent de document fonctionnel.
Conclusion
Concordion est un outil fort agréable en Java, mais malheureusement en .NET l’intégration n’est pas terminée. Si malgré tout vous souhaitez profiter de la liberté du style dans l’écriture des tests, alors nous ne pouvons que vous conseiller de contribuer sur le projet afin de simplifier la vie de vos futurs développeurs.
Fitnesse
[Résultat des tests fitnesse de TodoYouToo]
Lui aussi issu du monde Java, Fitnesse est un système complet de tests rédigeables sous la forme d’un wiki. L’exécutable Fitnesse est un serveur web autonome, qui prend en charge toutes les fonctionnalités du wiki (ajout / suppression de tests).
Pour jouer les tests, il fera appel à un “runner”. Dans notre cas, en .NET, il existe FitSharp qui remplira cette fonction.
Il est possible d’avoir un serveur Fitnesse maître, et plusieurs serveurs esclaves, qui se synchroniseront sur le maître.
Fitnesse et Fitsharp sont tout les deux open-source.
Et concrètement, comment ça marche ?
Un Fitnesse Master est déployé sur un serveur. Le PO ou les Business Analysts rédigent la documentation et les tests sur ce serveur.
Les développeurs quant à eux, auront sur leur poste de développement un Fitnesse esclave, qui se mettra à jour sur le maître tout au long du développement. Sur cette version locale, il sera possible au développeur de jouer les tests sur le code qu’il est en train de développer.
Dans la même logique, les binaires de l’application seront déployés sur le serveur Maitre. Cela permettra aux PO/BA de jouer les tests et en rédiger de nouveaux, et de les tester directement sur la dernière version de l’application.
Les pages peuvent contenir de la documentation, contenant des parties testables. Lorsque la page est exécutée au sein du navigateur, les parties testables sont évaluées, et colorées en fonction du résultat (Vert / Jaune / Rouge).
Exemple d'execution d'un test Fitnesse
D’un point de vue Business Analysts…
Fitnesse est donc un wiki, avec ses avantages, et ses inconvénients. Les spécifications seront rédigées directement dans un navigateur web, sous la forme d’un éditeur de texte enrichi (police / couleur / graisse / etc). Les liens entre les pages se feront sous la forme de WikiWords (ce sont les mots écrits en CamelCase qui sont automatiquement relié à la page en question).
Au sein de ses pages, le rédacteur pourra ajouter des blocs exécutables. Ils détermineront le succès d’une page à l'exécution. Ce système est très souple et permet de nombreuses choses :
- Exécuter une instruction
- Appeler une fonction et comparer le résultat
- Récupérer une liste et valider le résultats
- etc...
Il est possible d’organiser ses pages au seins d’une hiérarchie, d’ajouter des tags (pour filtrer les tests joués), de définir les pages exécutables, etc.
A noter que Fitnesse dispose d'une fonction d'import / Excel, ce qui peut être un avantage certain si vous êtes plus à l'aise pour rédiger vos cas d'exemple sous Excel.
D’un point de vu développeur…
Le développeur devra faire le code qui servira de glue entre les tests Fitnesse et le système testé. Schématiquement, une fixture devra être représentée par une classe, et chaque action devra être une fonction de cette classe. Un exemple vaut mieux qu’un long discours !
Si j’ai un test Fitnesse qui contient :
!| script | Fruits Seller | | Add a fruit | Apple | with color | Red |
En tant que développeur, je devrais implémenter la classe suivante :
public Class FruitsSellerFixture { public void AddAFruitWithColor(string name, string color) { // Call System Under Test. We have : // name = “Apple” // color = “Red” } }
Un monde parfait ?
Le système est alléchant, mais quelques inconvénients sont à noter :
Les pages d’un wiki sont triées par ordre alphabétique. Difficile d’organiser ses pages comme on le souhaite de par cette contrainte.
Il n’y a pas d'auto-complétion ou d’aide à l’écriture des Fixtures coté Wiki. Pour minimiser l’effort de développement et de maintenance, il est important d’utiliser au mieux ses fixtures. Hors, un rédacteur n’aura aucun moyen de savoir rapidement si la fixture existe déjà, ni quelle syntaxe il doit reprendre.
Très gourmand en RAM, il vaut mieux prévoir en conséquence les machines lorsque vous avez de nombreux tests.
Il n’y a aucune intégration possible avec Visual Studio. Cela n'empêche pas de débugger (en s’attachant au processus), mais cela ajoute quelques contraintes au développeur.
Enfin les tests ne vivent pas avec le système au sein d'un gestionnaire de source, il vous revient la charge de gérer les différentes versions.
L'exécution des tests fitnesse se révèle bien plus lente que les autres framework qui utilisent les lanceurs de tests unitaires.
Conclusion
Fitnesse est un très bon choix si l’on souhaite mettre en place des tests fonctionnels en .NET, et que nous avons déjà mis en place sur de gros projets.
Si vous avez dans l’équipe des gens très “fonctionnels”, Fitnesse sera vraiment un candidat à tester en priorité, grâce à son système de wiki très simple à prendre en main.
Si vous souhaitez un outil plus proche du développeur (refactoring, intégration dans votre gestionnaire de code source, etc..), Fitnesse ne sera peut-être pas le plus adapté.
Specflow
[Résultat des tests Specflow de TodoYouToo]
Specflow est un outil de test fonctionnel inspiré de Cucumber. Il est basé sur des tests écrits avec le langage Gherkin qui permet de décrire la logique métier grâce à une syntaxe simple : Given, When, Then.
Chaque mot clé a une signification particulière :
- Given : on déclare l’état du système
- When : l’action que l’on souhaite tester
- Then : la vérification que l’on souhaite faire
Les fixtures permettent de lier les spécifications avec le code applicatif que l’on souhaite tester :
il suffit de décorer les méthodes avec les attributs (annotations) nommées comme les tests de la feature.
L’intégration avec Visual Studio
La grande force de Specflow est son intégration avec Visual Studio. En effet, il est possible de lancer les tests avec Nunit ou MsTest avec la bonne bibliothèque ajoutée avec Nuget. Il existe une extension Visual Studio qui permet de générer automatiquement les squelettes de fixtures à développer. Tous ces outils sont gratuits.
Il en existe d’autres payants : Specflow+ et Speclog. Nous ne les avons pas testé mais Speclog semble intéressant. il permet de lier son backlog avec des tests Specflow.
De plus, Visual Studio apporte de l’auto-complétion au sein des fichiers de spécifications, et une coloration syntaxique permettant de voir rapidement les steps déjà implémentés.
Pickles permet d’exporter les tests sous différents formats (HTML, DOC ou EXCEL) afin de publier sa documentation fonctionnelle.
Specflow dans la vraie vie
Dans notre utilisation de l’outil, nous avions des rédacteurs qui viennent de la technique, ils utilisent donc Visual Studio pour écrire les tests en Gherkin. Si cela est trop compliqué pour le rédacteur, des alternatives existent : des éditeurs web ou alors des plugins Sublime ou atom. Concernant le cycle de vie de ces éléments, on peut imaginer un script qui synchroniserais les fichiers sur une branche du gestionnaire de source.
Enfin le développeur récupère les tests et peut les automatiser à l’aide des fixtures.
Conclusion
D’un point de vue développeur, cet outil est le plus agréable car il s’intègre très bien dans l’environnement de développement : il peut lancer ses tests en tant que tests unitaires. La difficulté viendra plus pour le rédacteur des tests s'il est étranger à la technique.
Conclusion, quel outil choisir ?
Pour nos futurs projets .NET, à moins d’avoir besoin d’une réelle flexibilité dans l’écriture de nos tests, nous n’utiliserons pas Concordion. Celui-ci n’est pas, à notre avis, encore mature pour l’environnement .NET.
Nous allons préférer Fitnesse si les personnes en charge de l'écriture des tests n’ont aucun passé technique : le langage Wiki est simple et il n’y a aucun outil à maîtriser à part le navigateur Web.
Specflow est plus simple d’utilisation pour les développeurs. S’il n’y a pas de contrainte forte sur les rédacteurs de spécifications, nous partirions sur cet outil. Si besoin, il reste possible d'utiliser les outils satellites qui permettent de rédiger leurs tests ailleurs que dans Visual Studio !
Peu importe votre choix, il ne faut pas oublier que les tests fonctionnels que vous mettez en place vous faire partie intégrante de votre logiciel. Au même titre que votre code métier, il faudra en prendre soin, le maintenir à jour et le faire évoluer.
Enfin ces outils offre un support de communication qui permet favoriser le dialogue entre les différentes parties d'un projet.