SpecFlow : Améliorer la lisibilité avec les StepArgumentTransformation

# BDD# test# Agile# SpecFlow
écrit par Damien publié le vendredi 1 décembre 2017
Dans cet article, je vous propose de voir comment augmenter la lisibilité et la maintenabilité des scénarii SpecFlow. En effet, à mesure que nos scénarii grossissent, il deviennent de moins en moins lisible et de moins en moins maintenable. Nous allons voir comment l'attribut StepArgumentTransformation peut nous aider à répondre à ces problématiques. L'application suivante gère les commandes des clients. Nous écrivons donc un scénario pour tester l'affichage du récapitulatif des commandes.
Scenario: Consulter total des commandes
	Given les clients suivants
	| nom   | prenom |
	| Bob   | Durant |
	| Marie | Dupont |
	And une liste de produits
	| nom      | description       | prix |
	| eau      | un pack d eau     | 2    |
	| chocolat | chocolat noir 85% | 1,6  |
	And les commandes
	| client       | produit  | quantite |
	| Bob Durant   | eau      | 2        |
	| Bob Durant   | chocolat | 1        |
	| Marie Dupont | eau      | 3        |
	When j affiche le total des commandes
	Then j obtient le récapitulatif suivant
		| client       | total |
		| Bob Durant   | 5,6   |
		| Marie Dupont | 6     |
Nous allons nous concentrer sur l'implémentation du step suivant "Given les clients suivants" On pourrait, de manière simple, l'écrire de la manière suivante :
        [Given(@"les clients suivants")]
        public void GivenLesClientsSuivants(Table table)
        {
            var clients = new List<Client>();

            foreach (var row in table.Rows)
            {
                clients.Add(new Client
                {
                    Nom = row["nom"],
                    Prenom = row["prenom"]
                });
            }

            ScenarioContext.Current.Set(clients);
        }
Cette implémentation fonctionne parfaitement, pourtant elle possède les inconvénients suivants :
  • la classe de définition du step à la responsabilité de mapper la table vers une liste de clients, ici nous sommes dans un cas simple, mais dans un cas un peu plus complexe on aurait rapidement beaucoup de code, multiplié par le nombre de steps ayant besoin de mapping on arrive vite à un fichier pas très lisible
  • notre méthode GivenLesClientsSuivants prend comme paramètre un objet Table, ce qui n'est pas très parlant. Ainsi on est obligé de lire la méthode pour comprendre qu'on cherche à obtenir une liste de client. Ce n'est donc pas très lisible.
Pour améliorer cette implémentation, je vous propose d'ôter la responsabilité du mapping à notre step. Pour réaliser ceci, nous utiliserons l'attribut [StepArgumentTransformation] fournies par SpecFlow. Dans un premier temps, nous allons créer une classe dans laquelle nous allons déplacer le mapping. Voici le code :
    [Binding]
    public class ClientMapper
    {
        [StepArgumentTransformation]
        public List<Client> TransformTableToClient(Table clientTable)
        {
            var clients = new List<Client>();

            foreach (var row in clientTable.Rows)
            {
                clients.Add(new Client
                {
                    Nom = row["nom"],
                    Prenom = row["prenom"]
                });
            }

            return clients;
        }
    }
Puis nous allons modifier le step de façon à ce qu'il ne prenne plus une Table en paramètre, mais une liste de clients
        [Given(@"les clients suivants")]
        public void GivenLesClientsSuivants(List<Client> clients)
        {
            ScenarioContext.Current.Set(clients);
        }
Grâce à l'attribut StepArgumentTransformation, SpecFlow va être capable d'injecter des listes de clients dans les steps en appelant notre code de mapping de manière automatique. Contrairement à la version précédente, on peut constater que les deux principaux inconvénients ont été corrigés :
  • le step a gagné en lisibilité grâce au déplacement du code de mapping et au changement de signature (on sait directement qu'on cherche a manipuler des clients)
  • les responsabilités sont mieux réparties entre les classes du fait que la classe contenant les steps n'a plus à se préoccuper de la transformation des Tables en objet métier
Il ne faut pas perdre de vue que même l'écriture des tests doit respecter les bonnes pratiques. L'utilisation de l'attribut [StepArgumentTransformation] nous le permet en offrant une meilleure lisibilité et permettant de mieux séparer les responsabilités lors de la conception d'une feature.

Ressources

SpecFlow :

GitHub Site officiel

Blog :

BDD sur .net Core Créer un scénario