Devenez un développeur fanTESTique

Le web est un milieu paradoxal : alors que ses développeurs sont à l’avant-garde pour tester des nouvelles librairies et fonctionnalités, en comparaison d’autres secteurs ils ont beaucoup de retard pour adopter des bonnes pratiques présentes ailleurs depuis des années. En l’occurrence, j’aimerais vous parler des tests automatisés, qui peinent à se démocratiser. J’ai même entendu récemment un chef de projet me dire « non, on n’a pas de tests automatisés. On fait confiance à la rigueur de nos développeurs ». Un frisson m’a parcouru le dos…

On ne peut pas faire confiance à un développeur

Malgré tout ce qu’ils diront, il faut accepter cette réalité : le développeur n’est pas une espèce digne de confiance. Je le sais, j’en suis un, et j’en fréquente un nombre conséquent. Certains sont néanmoins meilleurs que d’autres et écrivent du code plus fiable, mais malgré toute notre bonne volonté, il ne nous est pas possible d’écrire du code qui ne contienne pas d’erreurs.

Les raisons sont nombreuses : taille du projet, complexité de la base du code, changements dans une API qui rend obsolète une portion du code néanmoins toujours utilisée, cas non traités, erreurs d’inattention… Longtemps, nous avons testé manuellement nos modifications, mais l’époque du bricolage est sur le déclin. Des outils existent, il ne faut pas s’en priver. Bref, des outils sont nécessaires pour pallier ce problème.

Pourquoi des tests automatisés ?

Le problème de tout gros projet, c’est les régressions : à la suite d’une modification, un code qui fonctionnait ne fonctionne plus correctement. Il existe deux problèmes pour le développeur :

  • il faut détecter les nouveaux bugs avant de livrer. On peut relancer toute les pages  ayant subi un impact par des modifications, et tester à nouveau les pages déjà présentes ;
  • une fois un bug détecté, il faut le corriger. Et une fois corrigé, il faut revérifier, c’est-à-dire refaire tout ce qui a été dit au-dessus, parfois à plusieurs reprises.

Ces deux problèmes ont un point commun : il s’agit de tâches longues et pénibles. Détail amusant : on a inventé l’ordinateur pour résoudre ce genre de problèmes…

Les tests automatisés répondent donc à cette problématique : ils permettent de capitaliser sur le code, et avancer en confiance. Quand on a écrit un test automatisé, on sait que le code se comporte comme prévu, comme si on faisait la vérification manuellement. Lorsqu’on modifie une portion de code, on relance les tests. Si le test a été bien écrit et qu’il passe toujours, c’est que rien n’a été cassé. S’il ne passe plus, il faut comprendre pourquoi.

Avec des tests automatisés, il suffit en général d’une commande dans la console et de quelques secondes pour tout revérifier, là où à la main cela prendrait au mieux plusieurs minutes.

Le frein à l’adoption de ce genre de méthodes, c’est souvent l’investissement pour apprendre à intégrer ces pratiques dans son travail. Mais une fois que c’est fait, écrire des tests en parallèle de son code (voire avant si l’on souhaite pratiquer TDD) s’avère rapidement moins chronophage que devoir relancer manuellement plusieurs pages de l’application pour voir si elles marchent bien comme attendu. Lorsque vous vous y mettrez, vous serez rapidement gagnant.

Deux grandes familles de tests

Dans le domaine des tests automatisés, il existe deux grandes catégories, qui répondent à des problématiques différentes.
D’un côté, les tests unitaires. Ils ont pour but de vérifier le comportement d’une méthode en isolation. Quand je fais user.GetRights(), est ce que j’ai bien la bonne liste de tous les droits pour l’utilisateur concerné (pas de droits en plus ou en moins) ? C’est le type de test que l’on écrit pour valider le comportement d’une librairie, ou d’une API.

Côté backend, les outils sont assez connus pour faire des tests unitaires. En Python, jetez un  coup d’œil au module unittest. En PHP, PHPUnit a encore de beaux jours devant lui, mais Atoum fait sa place depuis peu, au niveau open source comme dans des agences. En Ruby, test/unit est la librairie par défaut du framework Rails, mais RSpec et Cucumber vous permettront de travailler différemment en intégrant la méthodologie BDD (Behaviour Driven Development).

En JavaScript, on peut écrire des tests pour le frontend comme pour le backend, et plusieurs librairies font parler d’elles : Jasmine, Mocha, QUnit…Il y a l’embarras du choix, il suffit de trouver celle dont le style vous plaît.

L’autre grande famille, c’est les tests fonctionnels. Ils vérifient que le comportement de l’application est bien celui attendu : la page d’accueil affiche-t-elle bien seulement les cinq dernières news ? Que se passe-t-il lorsqu’on valide le formulaire de création de compte sans fournir d’email ? Cet article de Smashing Magazine montre comment tester unitairement et fonctionnellement sa page, de manière basique. Cela peut vous aider à saisir la différence.
Pour réaliser ce genre de tests, il faut des outils pour récupérer une page, la traverser, remplir des champs, cliquer sur des liens ou valider des formulaires… exactement comme vous le feriez manuellement. Les outils précédents ne sont alors plus suffisants.

L’outil passe-partout, c’est Selenium. Mais si vous désirez un outil dédié, pour PHP jetez un œil à Goutte. Côté Python, Ghost.py fournit les outils nécessaires pour écrire des scénarios de traversée de page. En Rails il existe Capybara pour faire tout cela. Et en JavaScript, vous pourrez utiliser CasperJS pour faire vraiment des choses très sympa.

De plus, ce type d’outil peut également s’intégrer avec le framework que vous utilisez. J’avais par exemple traduit un article traitant de la manière de tester fonctionnellement des contrôleurs depuis le client de Symfony2.

Bref, non seulement pas mal de choses sont possibles pour le web, mais les outils sont nombreux et ont déjà fait leurs preuves. Tirez-en profit !

Attention !

Lors de votre apprentissage puis pendant votre utilisation des tests automatisés, gardez bien en tête qu’il s’agit d’un outil, et non d’une solution miracle. Si vous écrivez du code de mauvaise qualité, vos tests n’amélioreront pas la situation (de même pour TDD). Ne devenez pas non plus son esclave. Il peut être tentant de modifier un test pour qu’il devienne concluant sans toucher au code de la fonctionnalité, ou d’écrire des tests sans assertions simplement pour améliorer le taux de couverture de code (j’ai vu les deux). Si vous utilisez mal cet outil, il peut se retourner contre vous et se révéler contre-productif.

Bref…

Cet article est clairement une introduction sur le sujet. Beaucoup de choses restent à dire, mais j’espère vous avoir donné les outils pour tester vos applications. Si vous ne le faites pas, commencez à tester votre code. Apprenez ensuite à écrire de bons tests, ou à en écrire de meilleurs. Renseignez-vous sur les méthodologies TDD et BDD que j’ai à peine citées (par exemple ici et ), puis intégrez ces méthodologies si elles correspondent à votre style et à vos projets. Bref, apprenez à utiliser ces outils et utilisez-les pour devenir de meilleurs développeurs.

4 commentaires sur cet article

  1. Jo, le mardi 18 décembre 2012 à 09:13

    Merci pour cette introduction et sensibilisation à la mise en place de tests automatisés. Ce n’est pas encore dans mes habitudes, mais cet article m’a peut être poussé a me renseigner un peu plus sur le sujet et essayer de bien mettre cela en place dans mes futurs projets !

  2. Alex, le mardi 18 décembre 2012 à 09:42

    Oui, mais est-ce qu’on peut faire confiance à la rigueur des personnes qui écrivent les tests ? ;-)

  3. Clément Keirua, le mardi 18 décembre 2012 à 11:41

    C’est une question importante Alex et je n’ai pas eu le temps d’en parler dans cette introduction.
    Effectivement il ne faut pas faire n’importe quoi, et cela demande un investissement. Au départ pour apprendre à écrire de bons tests, et par la suite pour permettre au développeur de le faire.
    Je suis bien placé pour en parler : dans un projet précédent (quand je travaillais en SSII), nous avions pour engagement contractuel de livrer du code couvert à 80% par des tests unitaires. Par contre, nous avions des engagements de temps assez forts et qui permettaient péniblement d’écrire le code (et pourtant, nous ne traînions pas), que ce soit notre équipe ou d’autres prestas. Par conséquent les tests étaient écrits à la va vite et souvent, dans ce qui avait été fait avant, beaucoup de choses n’étaient pas très bonnes. J’ai pu trouvé des gros abus, comme par exemple des tests qui appellent une méthode mais ne contiennent pas d’assertions. La couverture de code augmente, le nombre de tests augmente, les chefs félicitent l’équipe qui a livré dans les temps, mais niveau qualité, c’est une catastrophe : ce que l’on croit être une brique solide sur laquelle on capitalise est en fait un cache misère.
    Il y a des choses à faire pour éviter ça (des sessions de revue de code par un développeur tiers par exemple…), mais l’essentiel est déjà d’avoir conscience que les tests automatisés ça prend du temps.
    L’autre aspect important des tests, c’est leur maintenabilité. Quand le projet évolue et qu’on refactore le code, on peut être amené à réécrire des tests. Il y a pas mal de choses à dire, mais l’essentiel, c’est de garder en tête que le but d’un test n’est pas de rester passant. Le but, c’est de tester que ça marche. Ne modifiez pas un test pour le rendre passant sans modifier le code qui lui est associé. Si vous commencez à faire cela, vous commencez à mal utiliser l’outil et par la suite, il ne garantira plus la qualité de votre code.
    Bref les tests automatisés c’est un outil mais c’est seulement cela et ce n’est en aucune manière une solution miracle. Utilisé correctement c’est très puissant, mal utilisé cela peut vous faire perdre beaucoup de temps.

  4. Sanpi, le vendredi 4 janvier 2013 à 17:16

    Il y a un petit mélange des outils pour PHP : selenium et goutte servent à effectuer des requêtes http (selenium pilote un vrai navigateur, goutte le fait via du code).

    Pour faire du BDD (et donc écrire des tests fonctionnels) en PHP, il existe Behat qui est calqué sur cucumber.