Menu
Menu

Nous contacter

01 82 83 51 70 infos@globalis-ms.com

6B rue Auguste Vitu

75015 Paris, France

Tests d’applications : comment y passer le moins de temps possible?

Tribune Glob'codeur

Le 19 mai 2019 par Quentin Diaferia

« Retour

Des applications Web testées automatiquement

Si l’écriture des tests est inévitable, il est possible d'en automatiser une bonne partie. Ainsi, vérifier des applications n’est plus cette lourde épreuve que certains connaissent ! Chez Globalis, nous avons choisi et recommandons Codeception pour tester nos applications web. Explications, décryptage des solutions et partage d’expériences.

Lorsque l'on développe une application web, ou à vrai dire n'importe quelle application, le plus gros du travail consiste bien sûr à concevoir et réaliser ses fonctionnalités une par une, du simple formulaire de connexion à une application complexe. Mais vient le moment où il faut s'assurer que l'application fonctionne correctement, que les fonctionnalités sont conformes aux spécifications, et que les nouveaux développements n’entraînent aucune régression sur le reste de l'application. Pour tout ça, pas le choix : il faut mettre en place des tests. Le simple fait de les écrire permet de détecter la plupart des problèmes. Si leur écriture est inévitable, il est possible d'automatiser une bonne partie de ces tests pour que vérifier nos applications ne deviennent pas une trop lourde épreuve.

Codeception : les tests efficaces

Pour tester nos applications web, nous avons fait le choix de Codeception, framework de test basé sur PHPUnit et disponible via Composer. En plus des tests unitaires déjà proposés par PHPUnit, Codeception permet d'implémenter des tests fonctionnels et des tests d'acceptance, ce qui rend possible de tester nos applications à l'échelle d'une simple fonction ou d'une fonctionnalité complète.

Afin de permettre l’exécution des tests d'acceptance, il est nécessaire d'utiliser un web driver tel que Selenium ou Chromedriver, grâce auquel Codeception sera capable de prendre le contrôle du navigateur et tester l'application en simulant les interactions utilisateurs : clics souris, entrées clavier, etc.

En cas d'échec des tests, Codeception fourni toutes les informations nécessaires pour corriger les erreurs trouvées : l'étape de test qui a échoué, celles qui la précèdent, les retours d'API attendus et effectivement reçus, ou la capture d'écran de la page web incriminée.

Le framework propose également de nombreux modules additionnels, parmi lesquels :

  • Un module WebDriver, qui permet de simuler des actions utilisateurs,
  • Un module base de données permettant par exemple d’interagir avec une base de données pour insérer des données ou les vérifier,
  • Un module CLI pour tester un script PHP appelé via la console,
  • Un module REST permettant de simuler des requêtes HTTP et d'en tester les réponses,
  • Un module Asserts, qui permet d'utiliser de simples assertions.

Tout pour tester efficacement une application web !

Écrivons par exemple un test d'acceptance pour un formulaire d'authentification via Codeception et son module WebDriver :

$I->am('user');
$I->wantTo('login to website');
$I->amOnPage('/login');
// Identifiants invalides
$I->submitForm('login_form', [
    'username' => 'user',
    'password' => 'invalid_password'
]);
$I->seeInCurrentUrl('/login');
$I->seeErrorInFields([
    'username'
]);
// Identifiants valides
$I->submitForm('login_form', [
    'username' => 'user',
    'password' => 'valid_password'
]);
$I->seeInCurrentUrl('/home');

Codeception permet aussi l'utilisation d'annotations dans nos classes de tests. On peut en contrôler l'ordre d'exécution avec @before et @after, ou utiliser @depends pour empêcher le déroulement d'un test si celui dont il dépend échoue. D'autres annotations permettent d'injecter des données dans nos tests : @example et @dataProvider, qui permettent respectivement de définir des jeux de données directement dans l'annotation, ou d'indiquer à Codeception une méthode renvoyant un tableau contenant ces données.

Maintenant, quels tests écrire pour tester efficacement une application ? Par exemple, pour un projet réalisé avec CodeIgniter proposant une plateforme Web ainsi qu'une API pour communiquer avec un système d'information extérieur, nous avons mis en place les tests suivants:

  • Tests unitaires chargés de tester les librairies métiers du projet.
  • Tests fonctionnels pour l'API REST, chargés de tester chaque endpoint de l'API (création, édition, lecture et suppression d'entités).
  • Tests d'acceptance pour la partie front de l'application, chargés de tester chaque module de l'interface : la connexion à l'application, les formulaires, l'administration, ainsi que les modules métiers, cœur de la plateforme.

Ainsi, toutes les facettes de l'application sont testées et on évite les mauvaises surprises.

Un autre framework de test, Behat, a également été développé pour PHP et rend possible l'écriture et l'exécution de tests d'acceptance d'applications Web. Cependant, il n'est pas possible d'écrire des tests unitaires et fonctionnels, il s'agit d'un outil véritablement destiné à tester le comportement des applications. Codeception, étant une extension de PHPUnit et proposant de nombreux modules (REST, CLI, Assert, etc), nous permet de couvrir toute notre application avec un seul outil qui pourra lancer tous ces tests d'une seule traite. De plus, Behat nécessite l'utilisation de Mink, équivalent du WebDriver de Codeception, ce qui a un impact sur la courbe d'apprentissage du framework. La force de Behat réside en la syntaxe qu'il propose pour rédiger les tests d'acceptance, Gherkin, syntaxe qui est également disponible dans Codeception.

Gherkin : idéal si vous n'êtes pas développeur

On l'a vu juste au-dessus, même si la syntaxe de rédaction des tests sur Codeception est plutôt claire, avoir des connaissances techniques reste quand même nécessaire. Aussi parlants soient les noms des méthodes, il faut prendre en main le framework et toutes ses notions et concepts : simple pour les développeurs, mais un chef de projet ou un client peut trouver tout cela obscur, et pourtant il est très intéressant qu'ils puissent lire les tests. Et si on pouvait rédiger des tests lisibles sans connaissances techniques particulières ? Et, soyons fous, si quelqu'un d'autre que le développeur pouvait les écrire ?

Une syntaxe a été mise au point par la plateforme de test Cucumber, dont Behat est une implémentation : Gherkin. Elle permet de définir nos cas de tests textuellement, à l'aide de mots clés : chaque test sera une suite de phrase définissant chacune une action : remplir un champ, cliquer sur un bouton, vérifier un texte, etc. Grâce à ce langage, il est possible d'écrire nos scénarios de la même façon que des spécifications, à l'image des users stories.

Si on reprend l'exemple de la page de connexion, mais cette fois-ci avec le format Gherkin :

Given I am on login page
 When I fill field "Identifiant" with "user"
  And I fill field "Mot de passe" with "invalid_password"
  And I submit form
 Then I should see error in field "Identifiant"
 When I fill field "Identifiant" with "user"
  And I fill field "Mot de passe" with "valid_password"
  And I submit form
 Then I should see title "Accueil"

Bien sûr, ces phrases ne sont pas interprétées magiquement par Codeception, il faut définir la façon dont Codeception va identifier chaque élément de nos pages web et interagir avec. Mais les éléments utilisés pendant les tests sont généralement communs à toute l'application : champs de formulaire, buttons d'action, listes, tableaux... Les sélecteurs ne seront à définir qu'une seule fois pour toute la durée du projet.

Par exemple, voici comment remplir un simple champ de type texte selon son libellé via un sélecteur XPath :

/**
 * @When I fill field :label with :value
 */
public function iFillFieldWith($label, $value)
{
    $this->fillField(
        [
            'xpath' => "//form"
                . "//div["
                    . "contains(@class, 'form-group')"
                    . " and label[contains(., '" . $value . "')]"
                . "]"
                . "//*[self::input]"
        ],
        $value
    );
}

La correspondance entre cette méthode et la phrase Gherkin se fait grâce aux annotations @When, @Then et @Given. Les tests d'acceptance de notre application deviennent donc de simples fichiers textes lisibles par tous les acteurs du projet, techniques ou non.

Allons plus loin

Nos tests d'acceptance sont en place, on peut lancer Codeception et admirer notre navigateur Chrome qui s'active tout seul à naviguer de page en page, tester tous les champs des formulaires, tous les boutons d'action, etc. Aussi hypnotisant que cela puisse être, c'est un peu dommage que l'écran soit monopolisé par ça, d'autant plus que le navigateur se ré-ouvre à chaque nouveau scénario et que la nouvelle fenêtre prend le focus... Pas pratique pour continuer à travailler. Pour pallier à ça, Chrome propose un mode "headless" : un simple ajout de l'option `--headless` dans le configuration de Codeception permet d'utiliser notre navigateur sans interface graphique. Les tests se déroulent exactement comme avant, mais sans ouverture intempestive du navigateur.

Pour le moment, nos tests et nous, développeurs, utilisons le même environnement. Une seule base de données est utilisée et cela peut conduire à des tests pas complètement maîtrisés. Chez Globalis, on aime bien Robo, on se sert donc de cet outil pour lancer nos tests, et construire notre application selon une configuration distincte de celle de l'environnement de développement : une base de données dédiée, avec des données de tests insérées exclusivement pour être utilisées au sein de nos tests. On peut donc imaginer une tâche Robo de test qui va :

  • Demander la configuration de test si elle n'a pas déjà été renseignée,
  • Reconstruire l'application selon cette configuration,
  • Réinitialiser nos données de test dans la base dédiée,
  • Jouer nos suites de test,
  • Reconstruire l'application avec l'environnement de développement.

Nos tests sont maîtrisés, mais le fait que l'application utilise la base de données de test peut gêner le développement pendant le déroulement des tests. L'idéal serait en fait de faire tourner les tests sur une autre machine pendant qu'on continue à développement tranquillement. Les équipes de Globalis utilisent GitLab comme outil de DevOps, et celui-ci propose notamment des fonctionnalités d'intégration continue. Il est possible d'utiliser un conteneur Docker afin de lancer des tâches automatisées directement via GitLab comme par exemple... des tests. Un simple fichier de configuration au format Yaml permet de définir ces tâches, et on peut ensuite lancer les tests sur une branche spécifique directement via GitLab. On a même la possibilité d'aller encore plus loin et de lancer les tests automatiquement lorsqu'on pousse une branche, ou encore lancer une tâche de déploiement lorsque les tests sont passés sans erreurs.

Liens externes

Article précédent Article suivant