3 maladies de l’automatisation des tests

Le testeur chargé de l’automatisation des tests est vulnérable à des troubles qui lui sont propres. Il est important de les connaître pour mieux s’en protéger.

La logopathie – Trouble cognitif

Symptôme

Le patient est le seul de son équipe à pouvoir lire les logs produits par ses tests automatisés. Souffrant d’une apparente dissonnance cognitive, il énonce des propos déroutants tels que « Tous ceux-ci ne passent pas mais c’est normal ».

Cause

Trop absorbé par le développement de ses automates, le patient a négligé la qualité du reporting, un élément pourtant nécessaire à la compréhension des résultats des tests.

Complications possibles

  • L’équipe devient dépendante de son testeur automaticien. Par conséquent, s’il est absent, il est impossible d’analyser les résultats des tests automatisés.
  • Analyser les logs devient si compliqué que, par manque de temps, on ne lance plus les tests automatisés. Tout le temps consacré à ce travail est désormais perdu.
  • Dans le pire des cas, on finit par abandonner l’automatisation des tests.

Mesures préventives

  • Utiliser un framework de test BDD : Gebish, Cucumber… qui prendra en charge nativement un reporting d’une clarté satisfaisante.
  • A défaut, anticiper cette problématique dès le début du projet d’automatisation. Pour chaque étape du test automatisé, générer un log en bon français. Il est également important de faire savoir où se trouvent les reportings, afin que les personnes concernées puissent les trouver en toute autonomie.

Remèdes possibles

Allouer du temps à cette problématique en communiquant sur son importance. Chiffrer les gains de temps possibles en analyse.

Si vous utilisez Selenium, une petite virée sur notre article dédié à la qualité des logs Selenium vous redonnera des couleurs.

Troubles associés

Le documutisme (trouble de la communication). Le patient n’a pas conscience de la complexité de son projet et/ou ressent une aversion envers les tâches documentaires. Il en résulte une aura de mystère autour des travaux d’automatisation des tests. Seuls quelques proches du patient ont été instruits de bouche à oreille et sont en mesure de comprendre, de loin en loin, ce dont il est question.

L’automatite – Trouble de l’identité

Symptôme

Le patient agit davantage en « automaticien » qu’en testeur. Il enchaîne les scénarios et automatise au kilomètre.

Cause

Le testeur chargé de l’automatisation des tests est soumis à une forte exposition à des problématiques purement techniques. Comment cliquer sur le premier bouton visible en ignorant les boutons cachés qui le précèdent ? Comment naviguer entre 2 popups ou plus ? Comment autoriser ou interdire la géolocalisation sur tel ou tel navigateur ? Cette charge cognitive peut le mener à oublier le pourquoi au profit du comment. Le patient devrait, en temps normal, être en mesure de conserver un œil critique sur les scénarios qu’il doit automatiser.

Complications possibles

  • Faute de recul, le patient perd un temps précieux à automatiser des scénarios à faible valeur ajoutée.
  • Le patient passe à côté de bugs qu’il aurait pu trouver.
  • Les reportings perdent en pertinence.

Mesures préventives

Dès le début du projet, prévoir des rôles hybrides. La dichotomie entre le testeur manuel et le testeur automaticien, décriée par certains, pourrait être néfaste au métabolisme de l’équipe de test.

Vu sur https://mrslavchev.com

Remèdes possibles

Redonner une vision d’ensemble de la qualité du produit en introduisant des phases de test manuel et de rédaction de cas de test dans l’emploi du temps du patient.

Troubles associés

Chez le testeur dit manuel, la checklistopathie paralyse les mêmes neurones que l’automatite. Les deux types de malades ont besoin, pour recouvrer la santé, de raviver leur curiosité et leur passion pour le fouinage logiciel.

La scalophobie – Trouble de la motricité

Symptôme

Le patient se trouve face à un projet d’automatisation des tests dont il n’a pas suffisamment étudié la scalabilité. L’architecture est brouillonne et difficile à maintenir. Le patient évolue avec difficulté au sein d’un écosystème qu’il a lui-même créé.

Cause

Avide d’avancer rapidement dans l’automatisation des scénarios, le patient a consacré un temps insuffisant à la conception de l’architecture de son projet d’automatisation.

Complications possibles

  • Importantes pertes de temps en maintenance.
  • Difficulté à intégrer de nouveaux collaborateurs dans le projet.
  • Risques accrus de contracter la logopathie

Mesures préventives

En début de projet, dédier une plage de temps suffisante à la conception de l’architecture du projet. Communiquer sur l’importance de cette phase. S’appuyer sur des design patterns éprouvés, tels que le Page Object (dont nous donnions dernièrement un exemple avec Protractor).

Remèdes possibles

Une bonne refactothérapie !

Troubles associés

La scalophobie de type documentaire, qui affecte les testeurs en charge de la rédaction des cas de test.

Conclusion

Il est essentiel de maintenir une bonne hygiène de vie afin d’éviter les maladies que nous venons de lister. Garder le cap sur les objectifs de l’automatisation, communiquer avec son entourage et conserver un recul sur ses activités permettra au testeur de rallonger l’espérance de vie de ses automates et de booster leur tonus au quotidien.

Bon courage !

Vous aimerez peut-être aussi…

Ce n’est pas un bug, c’est un poème

Les 7 principes généraux du test en illustrations

Le kit de secours métaphorique du testeur agile

Vous avez à coeur de respecter les bonnes pratiques de tests automatisés ? Cette page mémo pourrait vous être utile !

Protractor + AngularJS : Structure d’un projet industrialisé

Note préalable : cet article date de 2017. Depuis, Protractor a atteint la fin de sa vie. Merci à lui et surtout aux personnes qui ont contribué à cet outil !

Diviser pour mieux tester

Bienvenue dans la 3ème et dernière partie de notre série d’articles sur la découverte des tests Protractor ! Dans l’article précédent, nous avons vu pourquoi et comment séparer les jeux de données et les scripts de test. Aujourd’hui, nous poursuivons sur notre lancée en isolant la description des pages et en structurant les dossier du projet.

  1. Installation et exemple de script
  2. Gestion des jeux de données et des dépendances du projet
  3. Structure d’un projet industrialisé

Gestion du référentiel d’objets avec le Page Object Model

Qu’est-ce que le Page Object Model ?

Le Page Object Model est un design pattern (ou patron de conception) qui consiste à regrouper en une même classe les identificateurs des composants web avec lesquels on souhaite intéragir, ainsi que les méthodes associées à ces composants. Le Page Object Model joue le même rôle dans les projets Selenium que l’Object Repository dans les projets UFT ou Katalon par exemple.

Dans les articles précédent, nous interagissons avec la page d’accueil du site d’Angular. Si nous nous servons d’un objet page pour la décrire, cela donne :

var SiteAngularPageAccueil = function() {
    this.champ_nom = element(by.model('yourName'));
    this.message_salutations = element(by.xpath("//div[contains(@class, 'well')]//h1"));

    this.entrerPrenom = function(prenom) {
        this.champ_nom.sendKeys(prenom);
    };
};

module.exports = SiteAngularPageAccueil;

La méthode entrerPrenom est vraiment simpliste ; en pratique, on pourrait s’en passer. Nous avons choisi de la créer simplement pour fournir un exemple d’utilisation de méthode d’objet.

Enregistrons ce code dans un fichier site-angular-page-accueil.js, que nous plaçons dans un dossier « pages ».

Utilisation de l’objet page dans le script

Mettons à présent à jour le cas de test en appelant l’objet page et en interagissant avec elle :

var donnees = require('./jeu-de-donnees-demo.js');
var using = require('jasmine-data-provider');
var SiteAngularPageAccueil = require('../pages/site-angular-page-accueil.js');

describe("Suite de test de la feature qui dit bonjour", function() {

    var pageAccueil = new SiteAngularPageAccueil();

    beforeEach(function () {
        browser.get(browser.baseUrl);
    });

    using(donnees.prenoms, function (data, description) {
        it("Chaîne de type " + description, function () {
            testMessageSalutations(data.prenom);
        });
    });

    using(donnees.faux_positif, function (data, description) {
        it("Si je rêvasse en écrivant mon script de test, j'obtiens un faux positif", function () {
            testMessageSalutations(data.prenomSaisi, data.prenomVoulu);
        });
    });

    function testMessageSalutations(prenomSaisi, prenomVoulu){
        pageAccueil.entrerPrenom(prenomSaisi);
        var prenomAVerifier = (prenomVoulu ? prenomVoulu : prenomSaisi);
        expect(pageAccueil.message_salutations.getText()).toEqual('Hello ' + prenomAVerifier + '!');
    }

});

Nous avons également créé une fonction testMessageSalutations pour éviter au maximum la duplication entre les deux cas de test.

Le script est maintenant concis et exempt de toute redite ! A ce stade, notre script de test a atteint un niveau de maintenabilité satisfaisant. Il peut jouer une multitude de cas de test différents, et n’est plus soumis aux changements de l’interface du site web.

Exemple de structure de projet Protractor

Pour augmenter la lisibilité de votre projet, nous vous suggérons de ranger dans des répertoires séparés vos fichiers de configuration, vos pages, vos suites de tests et vos jeux de données.

Le nombre de suites de test augmentant, n’hésitez pas à créer des sous-dossiers. Dans vos fichiers de configuration, il sera d’autant plus simple de sélectionner tel ou tel ensemble de suites de tests. Par exemple :

exports.config = {
  seleniumAddress: 'http://localhost:4444/wd/hub',
  specs: ['../specs/front-office/*.js'],
    baseUrl: 'https://www.monsupersiteweb.nc',
    framework: 'jasmine2'
};

Ici, toutes les suites de test se trouvant dans /spec/front-office seront jouées.

Vous avez maintenant tout ce qu’il vous faut pour démarrer votre projet de tests Protractor sur de bonnes bases. A vous de jouer maintenant ! Et vous souhaitez rester encore un peu avec nous, voici un petit bonus…

Bonus : le reporting Jasmine

Vous aurez beau écrire le plus beau code du monde, il y a peu de chances que celui-ci suffise à émouvoir aux larmes votre chef de projet, client ou patron. Un reporting clair et documenté est indispensable pour communiquer sur l’exécution des tests. C’est pourquoi nous ne voulions pas vous quitter avant d’évoquer la librairie Jasmine2 Screenshot Reporter.

Ajout de la librairie

Dans votre package.json, déclarez la dépendance voulue :

"dependencies": {
  // [...]
  "protractor-jasmine2-screenshot-reporter": "0.3.5"
}

Mise à jour du fichier de configuration

Il est maintenant nécessaire d’appeler la librairie dans le fichier de configuration :

var HtmlScreenshotReporter = require('protractor-jasmine2-screenshot-reporter');

var reporter = new HtmlScreenshotReporter({
    dest: '../reports',
    filename: 'reporting-' + getDate(true) + '.html',
    reportOnlyFailedSpecs: false,
    captureOnlyFailedSpecs: true,
    showQuickLinks: true,
    reportTitle: "Rapport de tests automatisés du " + getDate() + "",
    reportFailedUrl: true,
    pathBuilder: function(currentSpec, suites, browserCapabilities) {
        return currentSpec.fullName + "--" + browserCapabilities.get('browserName');
    }
});

exports.config = {
  seleniumAddress: 'http://localhost:4444/wd/hub',
  specs: ['../specs/dis-bonjour.js'],
    baseUrl: 'https://angularjs.org',
    framework: 'jasmine2',

    beforeLaunch: function() {
        return new Promise(function(resolve){
            reporter.beforeLaunch(resolve);
        });
    },

    onPrepare: function() {
        jasmine.getEnv().addReporter(reporter);
    },

    afterLaunch: function(exitCode) {
        return new Promise(function(resolve){
            reporter.afterLaunch(resolve.bind(this, exitCode));
        });
    }
};

function getDate(dansNomFichier) {
    var today = new Date();
    var dd = today.getDate();
    var mm = today.getMonth() + 1;
    var yy = today.getFullYear();
    var hh = today.getHours();
    var min = today.getMinutes();

    if(dd<10) dd='0' + dd;
    if(mm<10) mm='0' + mm;
    if(hh<10) hh='0' + hh;
    if(min<10) min='0' + min;

    if(dansNomFichier){
        today = yy + '-' + mm + '-' + dd + '--' + hh + 'h' + min;
    } else {
        today = yy + '/' + mm + '/' + dd + ', ' + hh + ':' + min;
    }
    return today;
}

Explication du fichier de configuration

dest: '../reports'

Avec cette configuration de la variable dest, si votre fichier de configuration se trouve bien dans un sous-dossier dédié (/src/confs par exemple), un sous-dossier « reports » sera créé dans le dossier « src ». A l’intérieur de ce dossier seront créés les reportings au format html, ainsi que les screenshots.

A chaque lancement, son contenu sera supprimé. Si vous souhaitez conserver les résultats des tests d’un lancement à l’autre, mettez à true la variable preserveDirectory lors de l’instanciation de votre HtmlScreenshotReporter :

preserveDirectory: true,

Dans ce cas, un sous-dossier avec un nom aléatoire sera créé à l’intérieur du dossier « reports ».

filename: 'reporting-' + getDate(true) + '.html',

La variable filename, sans surprise, définit le titre du fichier de reporting ; ici, nous avons choisi de lui ajouter la date et l’heure (voir la fonction « getDate » en bas du fichier).

reportOnlyFailedSpecs: false,
captureOnlyFailedSpecs: true,

Les variables reportOnlyFailedSpecs et captureOnlyFailedSpecs permettent de consigner tous les cas de tests dans le reporting, mais de ne prendre un screenshot qu’en cas d’échec.

showQuickLinks: true,

La variable showQuickLinks est à false par défaut. Ici, elle permet de générer au début du fichier des liens vers chacun des cas de test en échec. Pratique quand on se retrouve avec une très grande campagne de test.

reportTitle: "Rapport de tests automatisés du " + getDate() + "",

La variable reportTitle configure le titre qui s’affiche en haut du rapport html.

reportFailedUrl: true,

La variable reportFailedUrl permet d’afficher dans le reporting l’URL sur laquelle l’échec a eu lieu.

pathBuilder: function(currentSpec, suites, browserCapabilities) {
    return currentSpec.fullName + "--" + browserCapabilities.get('browserName');
}

La fonction pathBuilder permet de définir le nom des fichiers de screenshot. Ici, le nom est composé du titre du cas de test et du nom du navigateur.

Notre série sur Protractor est maintenant terminée. En espérant qu’elle vous aura apporté des éléments, nous sommes curieux d’avoir vos retours sur ce framework.

Pour en savoir plus sur les différents outils de tests automatisés ainsi que leurs usages et leurs bonnes pratiques, rendez-vous ici !

Protractor + AngularJS : installation et exemple de script

Note préalable : cet article date de 2017. Depuis, Protractor a atteint la fin de sa vie. Merci à lui et surtout aux personnes qui ont contribué à cet outil !

Pourquoi Protractor ?

Protractor est un framework de test dont le but est de faciliter l’automatisation des tests fonctionnels pour les SUT (system under test) développés avec AngularJS. Il se base sur WebdriverJS, l’implémentation Javascript de Selenium, et intègre des solutions telles que Jasmine, qui permet de rédiger des tests en BDD (behavior-driver development), ou encore Cucumber et Mocha.

Protractor reconnaît les concepts AngularJS, tels que les attributs ng-repeaterng-controllerng-model… qui sont employés dans le code HTML pour manipuler les composants web.

D’autres frameworks de test sont bien sûr capables d’automatiser les applications AngularJS, mais utiliser Protractor améliore :

  • la rapidité d’écriture des tests
  • leur maintenabilité (passer par des xpath ou des sélecteurs CSS serait moins robuste)
  • leur lisibilité

Inversement, il est possible d’utiliser Protractor pour automatiser les tests d’une application non-AngularJS (voir cet article).

Nous vous proposons donc une série d’articles sur Protractor :

  1. Installation et exemple de script (le présent article !)
  2. Gestion des jeux de données et des dépendances du projet
  3. Structure d’un projet industrialisé

Nous utilisons ici la version 5 de Protractor.

Installation de Protractor

Voici quelques prérequis avant de pouvoir vous lancer dans les tests avec Protractor :

  • Avoir NodeJS installé sur sa machine (ce qui permet d’utiliser npm)
  • Pour l’installation, lancer les commandes suivantes :

npm install -g protractor
webdriver-manager update

  • Pour démarrer le serveur Selenium sur votre machine, démarrer une invite et lancer la commande :

webdriver-manager start

EDIT de décembre 2020 : Si à ce moment-là votre invite de commande vous indique que « java n’est pas reconnu en tant que commande interne ou externe », assurez-vous que votre PATH contient bien le chemin vers le dossier « bin » qui se trouve dans le dossier où est installé Java. Ca peut ressembler à C:Program FilesJavajdk-xx.x.xbin. (Fin de l’EDIT)

Par défaut, le serveur Selenium est lancé sur le port 4444. Vous pouvez changer le port en ajoutant un paramètre à la commande.

webdriver-manager start –seleniumPort 5555

Tant que vous utiliserez Protractor sur votre machine, ne fermez pas cette invite.

Pour vérifier que le serveur est bien lancé, rendez-vous sur http://localhost:4444/wd/hub (en changeant le port si besoin).

Exemple de test Protractor

Au cours de ce test, nous allons interagir avec un composant de la page d’accueil du site d’AngularJS.

Sur cette page, quand l’utilisateur entre une chaîne de caractères dans le champ « Name », la phrase de salutations se met immédiatement à jour. C’est cette fonctionnalité que nous allons tester.

Dans l’inspecteur, on voit que l’attribut « ng-model » du champ de saisi a pour valeur « yourName » ; c’est cet attribut qui va nous servir lors de l’identification de l’objet.

Nous avons maintenant besoin de 2 fichiers : le fichier de configuration, qui permet de lancer le test, et le script de test en lui-même.

Un script de test Protractor

Le fichier de test (appelons-le dis-bonjour.js) a le contenu suivant :

describe("Suite de test de la feature qui dit bonjour", function() {

  it("Test 1 : si je renseigne mon nom, la feature me dit bonjour", function() {
    browser.get(browser.baseUrl);
    element(by.model('yourName')).sendKeys('Zoé');
    var message= element(by.xpath("//div[contains(@class, 'well')]//h1"));
    expect(message.getText()).toEqual('Hello Zoé!');
  });

  it("Test 2 : si je rêvasse en écrivant mon script de test, j'obtiens un faux positif", function() {
    browser.get('https://angularjs.org');
    element(by.model('yourName')).sendKeys('Chloé');
    var message= element(by.xpath("//div[contains(@class, 'well')]//h1"));
    expect(message.getText()).toEqual('Hello Zoé!');
  });

});

EDIT de décembre 2020 : si vous souhaitez lancer vos tests sur une application qui n’est pas une application Angular, il est important d’intégrer la commande suivante à vos scripts, afin qu’elle empêche Protractor d’attendre qu’Angular soit correctement chargé : browser.waitForAngularEnabled(false). (Fin de l’EDIT)

Structure et la syntaxe du script Protractor

Conformément à la syntaxe Jasmine, la suite de test se déroule dans la fonction « describe ». Chaque test est détaillé dans une fonction « it ».

L’URL de base (browser.baseUrl) est définie dans le fichier de configuration. Rien n’empêche bien sûr d’utiliser une autre URL (comme par exemple dans le deuxième test du fichier).

Fichier de configuration Protractor

Et maintenant, le fichier de configuration qui va permettre de lancer ces deux courts tests. Appelons-le conf.js. Celui-ci est minimaliste et ne contient que l’adresse du serveur Selenium, le chemin du script de test, et l’URL du SUT.

exports.config = {
  seleniumAddress: 'http://localhost:4444/wd/hub',
  specs: ['dis-bonjour.js'],
  baseUrl: 'http://monsupersiteweb.nc'
};

Lancer les tests Protractor en ligne de commande

Maintenant, pour lancer les deux tests contenus dans le script, il suffit d’ouvrir une invite de commande, de se positionner dans le dossier contenant les deux fichiers et de lancer la commande :

protractor conf.js

Remarque : il est possible de changer la baseUrl au moment du lancement du test, ce qui donne :

protractor --baseUrl=http://monsupersiteweb.nc conf.js

Ce qui est pratique si l’on souhaite faire tourner les tests sur différents environnements par exemple.

Par défaut, c’est un navigateur Google Chrome qui s’ouvre et exécute les scénarios définis dans le script de test Protractor. Ce qui donne, dans notre cas, le résultat suivant :

Plutôt clair, non ?

Protractor et les IDE

Pour l’écriture des tests, un simple éditeur de texte suffit pour si l’on veut juste tester les commandes de bases et se faire une idée rapide des possibilités du framework. Mais pour industrialiser un projet de tests automatisés, il est bien sûr préférable d’utiliser un IDE. Il existe des plugins Protractor pour IntelliJ et pour Eclipse. Il est à noter que celui d’IntelliJ n’est disponible que si l’on dispose d’une licence commerciale (version Ultimate, et non Community). Il est possible d’essayer gratuitement la version Ultimate pendant 30 jours.

Protractor avec Eclipse

EDIT de décembre 2020 : le bug évoqué est peut-être résolu depuis, nous n’avons pas réessayé. (Fin de l’EDIT)

Configurer le plugin Protractor d’Eclipse nécessite quelques manipulations en plus (détaillées dans cette vidéo). Attention toutefois, en mars 2017 un bug empêchait le plugin de tourner correctement.

Une erreur s'affiche : "SyntaxError: Unexpected token ..."

En effet, la configuration de Run Protractor utilise par défaut la version 4.2.4 de NodeJS, alors que 2 versions majeures ont été publiées depuis !

Indiquer un chemin vers un exécutable plus récent est possible, mais provoque une erreur : « Error while launching client file, Cannot find node install path node-native ».

A suivre donc… Pour la suite de cette série d’articles, nous utiliserons donc la solution d’évitement : passer sur la version d’essai d’IntelliJ.

Protractor avec IntelliJ

  1. Créez un projet.
  2. Dans le fichier src, placez vos 2 fichiers (test et configuration).
  3. Créer une « Run configuration » Protractor
  4. Renseigner le chemin vers le fichier de configuration. Les chemins vers NodeJS et Protractor sont, normalement, trouvée automatiquement.

Voilà ce à quoi ressemblent les résultats d’exécution :

Protractor avec Visual Studio Code

Ce paragraphe est un EDIT de décembre 2020.

Ouvrez une nouvelle fenêtre de Visual Studio code ; dans la partie de droite de l’écran s’affiche un encart appelé « Customize ». Dans la partie « Tools and languages », faites en sorte que VSC supporte JavaScript, TypeScript, et si vous le sentez vous pouvez également installer d’autres extensions telles que Mocha Snippets ou Protractor Snippets, qui proposent des raccourcis permettant d’accélérer la saisie des scripts de test Protractor.

Cette manipulation décuplera votre productivité par l’autocomplétion !

(Fin de l’EDIT)

On se retrouve demain pour aller plus loin dans l’exploration de Protractor. Car on ne veut pas seulement créer un test, on veut pouvoir le jouer avec une multitude de jeux de données !

Protractor + AngularJS : Gestion des jeux de données et des dépendances du projet

Note préalable : cet article date de 2017. Depuis, Protractor a atteint la fin de sa vie. Merci à lui et surtout aux personnes qui ont contribué à cet outil !

Le problème du tas

Au commencement d’un projet de test, surtout si celui est censé n’être qu’un POC ou un tout petit projet, on a tendance à coder les jeux de données en dur dans le script et à télécharger les librairies au fil de l’eau. Trèèès bieeeen… tant que ça ne dure pas ! Car comme écrivait Beckett en 1957 :

Les grains s’ajoutent aux grains, un à un, et un jour, soudain, c’est un tas, un petit tas, l’impossible tas.

Un tas qu’il n’est pas facile à gérer quand on revient sur le projet quelques mois plus tard, ou qu’un collègue doit travailler dessus pendant qu’on est en vacances à Ouvéa.

Aujourd’hui donc, on vous donne des pistes pour gérer les jeux de données et les librairies de votre projet. Cet article est le deuxième de notre série sur les test Protractor :

  1. Installation et exemple de script
  2. Gestion des jeux de données et des dépendances du projet
  3. Structure d’un projet industrialisé

Quels problèmes souhaitons-nous éviter ?

Pour comprendre l’intérêt de ce qui va suivre, penchons-nous sur ce petit texte :

« Vous a-t-on déjà parlé de la Nouvelle Calédonie, un archipel du Pacifique dont la capitale est Nouméa, qui compte quelque 300 000 habitants et dont la devise est le franc pacifique ? Recherchez le taux de change actuel entre le franc pacifique et l’euro. La Nouvelle Calédonie, un archipel du Pacifique dont la capitale est Nouméa, qui compte quelque 300 000 habitants et dont la devise est le franc pacifique, possède un lagon exceptionnel, classé au patrimoine mondial de l’Unesco. Recherchez depuis quand c’est le cas. L’oiseau fétiche de la Nouvelle Calédonie, un archipel du Pacifique dont la capitale est Nouméa, qui compte quelque 300 000 habitants et dont la devise est le franc pacifique, est le cagou, un oiseau à huppe presque incapable de voler. Trouvez d’où lui vient son nom. Il est très agréable de vivre en Nouvelle Calédonie, un archipel du Pacifique dont la capitale est Nouméa, qui compte quelque 300 000 habitants et dont la devise est le franc pacifique, cependant il faut tenir compte du fait que ce coin de paradis se trouve à quelque 16 000 kilomètres de la métropole. Vérifiez que Nouméa et la ville française la plus éloignée de Paris. »

Du point de vue du lecteur, ce texte est totalement redondant et illisible. Vous auriez préféré ne lire chaque information qu’une seule fois, et votre cerveau aurait rattaché chacune de ces données à la variable « Nouvelle Calédonie », qu’il possédait peut-être déjà. Cela vous aurait permis de vous concentrer davantage sur les questions.

Si l’on se place du point de vue de l’auteur, au prochain recensement de la Nouvelle Calédonie, il faudra qu’il remplace chaque occurrence de « 300 000 » par, peut-être, « 320 000 » ou « 350 000 ». Au fil du temps, le texte s’allongeant les mises à jour deviendront de plus en plus fastidieuses.

Ce texte est comme un projet de test mal architecturé, qui en un seul bloc fournit une multitude d’informations souvent répétitives sur le SUT, et sollicite dans la foulée des vérifications.

La bonne pratique

Autant que possible, nous allons donc prendre soin de séparer les moments où :

  • on configure les données du test,
  • on donne des informations sur le SUT (identificateurs d’objets, descriptions de page),
  • on effectue des vérifications (tel élément est présent, tel texte est visible).

Gestion des données de test avec jasmine-data-provider

En automatisation des tests, une bonne pratique consiste à séparer les données du test et les scripts d’exécution. Cela augmente la maintenabilité des tests et permet aussi à tout un chacun de mettre à jour le jeu de données, sans avoir à connaître l’architecture du projet sur le bout des doigts.

Qui plus est, cela permet d’alimenter un même script avec X jeux de données, et d’augmenter la couverture des tests à moindre coût .

Avec Protractor, le plugin jasmine-data-provider par exemple permet d’implémenter cette séparation données/scripts.

Pour l’utiliser dans l’exemple précédent, nous allons suivre trois étapes.

1) Importer jasmine-data-provider

A la racine de votre projet, téléchargez le package jasmine-data-provider :

npm install jasmine-data-provider --save-dev

Un dossier node_modules va se créer à la racine de votre projet et contenir le paquet voulu.

Il existe une autre manière de procéder, que nous verrons plus tard. En attendant, veillez juste à ne pas répertorier le dossier node_modules dans votre outil de versionning. Par exemple, si vous utilisez Git, ajoutez la ligne « node_modules/ » dans votre fichier .gitignore et votre projet restera léger comme une plume.

2) Modifier le fichier de configuration

Ici, il n’y a qu’une ligne à ajouter :

framework: 'jasmine2'

3) Créer un fichier de données

Ce fichier doit être au format .js. Appelons-le jeux-de-donnees-demo.js.

Ici, nous avons 2 séries de jeux de données. La première comprend 5 jeux de données avec chacun 1 paramètre : le prénom. Outre les caractères spéciaux courants dans la langue française, nous voulons savoir si d’autres caractères spéciaux (ici, arabes et chinois) apparaissent correctement, et si les scripts Javascript sont traités comme du simple texte.

La deuxième série, destinée à générer un faux positif,  a 2 paramètres : le prénom saisi et le prénom attendu.

'use strict';

 module.exports = {
    prenoms: {
        'ASCII': { prenom: 'Robert' },
        'Caractères spéciaux français': { prenom: 'Rôbértà' },
        'Caractères spéciaux arabes': { prenom: 'صباح الخير' },
        'Caractères spéciaux chinois': { prenom: '安' },
        'Script': { prenom: "<script type='text/javascript'>alert('Faille 1');</script>"}
    },

    faux_positif: {
        'Fail !': { prenomSaisi: 'Chloé', prenomVoulu: "Zoé" }
    }
}

La mention « use strict » est facultative mais conseillée, car elle garantit que le code est bien formé.

4) Exploiter le fichier de données au sein du script de test Protractor

Nous allons un peu transformer le script de l’article précédent :

var donnees = require('./jeu-de-donnees-demo.js');
var using = require('jasmine-data-provider');

describe("Suite de test de la feature qui dit bonjour", function() {

    beforeEach(function () {
        browser.get(browser.baseUrl);
    });

    using(donnees.prenoms, function (data, description) {
        it("Chaîne de type " + description, function () {
            element(by.model('yourName')).sendKeys(data.prenom);
            var message= element(by.xpath("//div[contains(@class, 'well')]//h1"));
            expect(message.getText()).toEqual('Hello ' + data.prenom + '!');
        });
    });

    using(donnees.faux_positif, function(data, description) {
        it("Si je rêvasse en écrivant mon script de test, j'obtiens un faux positif", function() {
            element(by.model('yourName')).sendKeys(data.prenomSaisi);
            var message= element(by.xpath("//div[contains(@class, 'well')]//h1"));
            expect(message.getText()).toEqual(data.prenomVoulu);
        });
    });
});

Au début du script, on indique que l’on souhaite utiliser jasmine-data-provider, ainsi que le chemin de notre fichier de données.

Pour le premier cas de test, on déclare qu’on va utiliser la série de données intitulée « prenoms », ce qui nous donne accès aux données (« data ») ainsi qu’au nom de chaque jeu de données (« description »). Pour accéder au paramètre « prenom », il suffit d’indiquer « data.prenom ».

Nous avons également choisi de factoriser, entre les deux cas de test, la première étape, à savoir la consultation de l’URL de base du projet :

beforeEach(function () {
    browser.get(browser.baseUrl);
});

Au lancement, la même méthode de test sera lancée autant de fois qu’il y a de jeux de données, avec les paramètres correspondants.

Gestion des données de test avec csv-parse

NB : cette section a été ajoutée en décembre 2020.

Si vous préférez exploiter des fichiers CSV, c’est possible aussi en utilisant le module csv-parse.

Commencez par installer ce module avec la ligne de commande suivante :

npm install csv-parse

Dans cet exemple, nous utiliseront un fichier de jeu de données nommé donnees.csv, et qui contiendra les informations suivantes :

login,password,prenom
admin,M0t2p@sseAdmin,Amira
user,M0t2p@sseUser,Boniface

Nous allons appeler ce jeu de données dans le script suivant, qui vérifie qu’à l’authentification sur un certain site web, on se retrouve sur une page où l’on voit la mention « Bonjour » suivie du prénom de l’utilisateur.

'use strict';
let fs = require('fs'); // fs pour file system. Il n'y a rien besoin d'installer pour utiliser cela.
const parse = require('csv-parse/lib/sync');
let a = fs.readFileSync('donnees.csv');

const testdata = parse(a, {
    columns: true,
    skip_empty_lines: true
})

describe('Suite de tests d'authentification', function () {
    for (let i of testdata) {       
        it('Connexion et affichage de la page d'accueil', async function () {
            browser.get(browser.baseUrl)
            console.log("Saisie du login : " + i.login)
            element(by.id('input-login-username')).sendKeys(i.login)
            console.log("Saisie du mot de passe : " + i.password)
            element(by.id('input-login-password')).sendKeys(i.password)
            element(by.id('button-login-submit')).click()
            console.log("Vérification de l'affichage des salutations")
            var message= element(by.xpath("//div[contains(@class, 'h1')]"))
            expect(message.getText()).toEqual('Bonjour ' + i.prenom)
        });
    }
});

Et voilà !

(Fin de l’EDIT)

Gestion des dépendances avec le package.json

Nous venons de voir comment rapidement ajouter la dernière version de jasmine-data-provider. Mais cette façon de procéder n’est pas la meilleure au sein d’un projet industrialisé, où certaines problématiques se posent :

  • Comment alléger le projet de tests Protractor ?
  • Comment faire en sorte que tous les membres de l’équipe utilisent la même version de jasmine-data-provider ?

C’est à ce moment qu’intervient le fichier package.json. Ce fichier, que l’on va placer à la racine du projet, contient une liste de tous les packages dont a besoin le projet. Si vous venez de l’univers Maven, cela devrait vous rappeler le pom.xml, ou le build.gradle si vous venez de la constellation Gradle.

Et la bonne nouvelle, c’est que npm propose une façon rapide de générer un fichier package.json ! Pour ce faire, placez-vous à la racine de votre projet, et tapez la commande :

npm init

Une série de questions sur votre projet vous est posée. Des réponses par défaut vous sont proposées. Pour un petit projet tel que nous venons d’en créer, l’opération dure moins d’une minute.

La valeur entrée pour le « main » dans l’utilitaire n’importe pas du tout ici. Vous pourrez la supprimer de votre package.json.

Pour le « test command », entrez la commande suivante :

node_modules/.bin/protractor src/conf-demo.js

A ce stade, le fichier généré ressemble à cela :

{
  "name": "hightest-demo",
  "version": "1.0.0",
  "description": "Un petit projet de démo pour présenter les tests Protractor",
  "dependencies": {
    "jasmine-data-provider": "^2.2.0"
  },
  "devDependencies": {},
  "scripts": {
    "test": "protractor src/conf-demo.js"
  },
  "author": "",
  "license": "ISC"
}

L’utilitaire a automatiquement détecté le package jasmine-data-provider, et l’a donc déclaré dans le package.json. Vous avez remarqué ce petit accent circonflexe juste avant le numéro de version ? Il fait partie de la syntaxe de versionning de npm (que nous vous invitons à découvrir ici) et permet ici de bénéficier des mises à jour non-majeures du paquet.

Cependant, nous vous conseillons autant que possible de fixer les versions de vos projets, afin d’être sûr d’avoir des configurations similaires d’une machine à l’autre.

Vérifions que tout marche bien : supprimons sauvagement le dossier node_modules créé précédemment, et lançons la commande « npm install » à la racine du projet. Et hop, le dossier réapparaît.

De bonnes fondations sont posées, mais il reste encore quelques éléments à prendre en compte pour que le projet soit industrialisable. A demain pour aller encore plus loin avec votre projet de test Protractor !

Découvrez nos autres séries d’articles !

Notre saga du test audiovisuel

Notre série dédiée aux tests de sécurité

Le kit de secours métaphorique du testeur agile