Skip to main content

Comment écrire votre premier test unitaire en JavaScript

Écrit par

Raphael Mun

feature java dto

28 octobre 2022

0 minutes de lecture

Tester le code est la première étape pour le sécuriser. Pour cela, l’une des meilleures méthodes consiste à utiliser des tests unitaires afin de vérifier que chacune des petites fonctions d’une application se comporte comme prévu, en particulier lorsque l’application reçoit des entrées limites, non valides ou potentiellement malveillantes.

Pourquoi écrire des tests unitaires ?

Il existe de nombreuses approches en matière de tests unitaires. Ils ont notamment pour objectifs de :

  • Vérifier les fonctionnalités : les tests unitaires garantissent que le code fait ce qu’il doit faire, sans exécuter d’actions non prévues — qui sont à l’origine de la plupart des bugs.

  • Éviter les régressions du code : lorsqu’un bug est détecté, ajouter un test unitaire pour vérifier le scénario évite que des modifications ultérieures ne le réintroduisent.

  • Documenter le code : avec les bons tests unitaires, un ensemble complet de tests et de résultats définit les spécifications du comportement attendu d’une application.

  • Sécuriser votre application : les tests unitaires peuvent détecter des vulnérabilités exploitables, comme celles qui permettent des injections SQL malveillantes.

Définir le périmètre et écrire des tests unitaires

Un framework de tests unitaires permet d’écrire et d’automatiser rapidement nos tests, puis de les intégrer à nos processus de développement et de déploiement. Ces frameworks prennent souvent en charge les tests du code JavaScript côté front-end comme côté back-end.

Voici quelques conseils généraux pour écrire des tests unitaires performants et produire du code facile à tester.

Faites des tests unitaires courts et simples

Ne surchargez pas vos tests unitaires. Un test ne devrait comporter que quelques lignes de code et vérifier de petites parties fonctionnelles de votre application.

Prévoyez des cas de test positifs et négatifs

Écrire un test pour vérifier qu’une fonction s’exécute correctement est utile. Mais il est bien plus efficace de rédiger un ensemble plus complet de tests, qui vérifient qu’une fonction échoue comme prévu en cas de mauvaise utilisation ou dans des situations limites. Ces tests négatifs peuvent être encore plus précieux, car ils permettent d’anticiper les situations inattendues : par exemple, lorsqu’une fonction doit lever une exception ou gérer la réception de données malformées.

Décomposez les fonctions longues et complexes

Les fonctions volumineuses qui contiennent beaucoup de logique sont difficiles à tester ; trop d’opérations empêchent de tester efficacement chaque variable. Si une fonction est trop complexe, divisez-la en fonctions plus petites à tester individuellement.

Évitez les connexions réseau et aux bases de données

Les tests unitaires doivent être rapides et légers, mais les fonctions qui effectuent des appels réseau ou se connectent à d’autres applications ou processus sont longues à exécuter. Il devient alors difficile d’exécuter de nombreuses opérations en parallèle, et le code peut être plus fragile. Vous pouvez utiliser le mocking dans les tests unitaires pour simuler des appels réseau ou à une base de données et tester le reste de la fonction. Vous pourrez intégrer les vraies connexions réseau et à la base de données dans un autre type de tests : les tests d’intégration.

Comment écrire des tests unitaires

Maintenant que nous avons passé en revue les bonnes pratiques des tests unitaires, vous êtes prêt à écrire votre premier test unitaire en JavaScript.

Ce tutoriel utilise le framework Mocha, l’un des plus populaires pour les tests unitaires. Chaque framework de test est un peu différent, mais ils se ressemblent suffisamment pour que la maîtrise des concepts de base vous permette de passer facilement de l’un à l’autre.

Pour suivre cette démonstration, vérifiez que Node.js est installé sur votre ordinateur.

Créer un nouveau projet

Commencez par ouvrir une fenêtre de terminal ou une invite de commandes dans un nouveau dossier de projet. Créez ensuite un projet Node.js dans ce dossier en saisissant npm init -y.

Cette commande crée un fichier package.json dans le dossier. Vous pouvez alors installer Mocha en tant que dépendance de développement à l’aide de npm install -D mocha.

Ouvrez ensuite le fichier package.json dans votre éditeur de code et remplacez le script de test par défaut par mocha :

  "scripts": {
    "test": "mocha"
  },

Implémenter une classe

Écrivons maintenant un système simple de feux de circulation à tester.

Dans le répertoire de votre projet, créez un fichier nommé traffic.js et ajoutez le code suivant pour une classe TrafficLight :

class TrafficLight {
    constructor() {
        this.lightIndex = 0;
    }

    static get colors() {
        return [ "green", "yellow", "red" ];
    }
    get light() {
        return TrafficLight.colors[ this.lightIndex ];
    }
    next() {
        this.lightIndex++;
        // This is intentionally wrong!
        if( this.lightIndex > TrafficLight.colors.length ) {
            this.lightIndex = 0;
        }
    }
}

module.exports = TrafficLight;

Cette classe comprend quatre éléments :

  • TrafficLight.colors : une propriété constante qui contient les couleurs du feu de circulation.

  • lightIndex : une variable qui suit l’index de la couleur actuelle du feu.

  • light : une propriété de classe qui renvoie sous forme de chaîne la couleur actuelle du feu.

  • next() : une fonction qui passe le feu à la couleur suivante.

Configurer et ajouter notre premier test unitaire

Il est maintenant temps d’ajouter des tests unitaires à notre code.

Créez un dossier nommé test dans le répertoire du projet. Mocha y recherchera les tests unitaires par défaut. Créez ensuite un fichier nommé traffic.test.js dans ce nouveau dossier.

Importez ensuite la classe TrafficLight en haut du fichier :

const TrafficLight = require( "../traffic" );

Nous allons également utiliser le module assert pour les tests. Ajoutez-le donc à votre code avec require :

const assert = require( "assert" );

Avec Mocha, nous pouvons regrouper les tests unitaires à l’aide de la fonction describe(). Nous pouvons donc créer un groupe de premier niveau pour cette classe :

describe( "TrafficLight", function () {
});

Nous allons ensuite créer un sous-groupe colors dans le groupe TrafficLight, et y ajouter des tests unitaires pour valider les couleurs du feu :

describe( "TrafficLight", function () {
    describe( "colors", function () {
    });
});

Pour ce premier test unitaire, vérifions que colors ne comporte que trois états : vert, jaune et rouge. Le test est défini à l’aide d’une fonction it() à l’intérieur du groupe describe(). Écrivez-le comme ceci :

describe( "TrafficLight", function () {
    describe( "colors", function () {
        it( "has 3 states", function () {
            const traffic = new TrafficLight();
            assert.equal( 3, TrafficLight.colors.length );
        });
    });
});

Exécutons maintenant le test unitaire pour vérifier qu’il réussit.

Exécutez npm test dans votre terminal. Si tout est correct, Mocha affiche les résultats de l’exécution du test unitaire.

Sortie du terminal affichant les tests Mocha pour les couleurs de TrafficLight, avec un test réussi en 8 ms.

Ajouter d’autres tests unitaires

Notre projet est maintenant prêt à exécuter des tests unitaires. Nous pouvons en ajouter d’autres pour vérifier que notre code fonctionne correctement.

Commencez par ajouter un test unitaire au groupe colors pour vérifier que les couleurs du feu sont correctes et dans le bon ordre. Voici une façon d’implémenter ce test :

it( "colors are in order", function () {
    const expectedLightOrder = [ "green", "yellow", "red" ];
    const traffic = new TrafficLight();
    for( let i = 0; i < expectedLightOrder.length; i++ ) {
        assert.equal( expectedLightOrder[ i ], TrafficLight.colors[ i ] );
    }
});

Testez ensuite la fonction next() pour vérifier qu’elle change correctement les feux. Créez un nouveau sous-groupe et ajoutez-y deux tests unitaires : l’un pour vérifier que les feux changent dans le bon ordre, l’autre pour vérifier que le feu repasse au green après le red.

Écrivez ces tests unitaires comme suit :

describe( "next()", function () {
    it( "changes lights in order", function () {
        const traffic = new TrafficLight();
        for( let i = 0; i < TrafficLight.colors.length; i++ ) 
            assert.equal( traffic.light, TrafficLight.colors[ i ] );
            traffic.next();
        }
    });
    it( "loops back to green", function () {
        const traffic = new TrafficLight();
        // Change the light 3x to go from green -> yellow -> red -> green
        for( let i = 0; i < 3; i++ ) {
            traffic.next();
        }
        assert.equal( traffic.light, TrafficLight.colors[ 0 ] );
    });
});

En relançant les tests, nous verrons que l’un d’eux échoue. Cela est dû à un bug dans la classe TrafficLight.

Résultat dans le terminal des tests JavaScript avec Mocha : trois tests réussis et un test échoué pour une assertion sur un feu tricolore.

Corriger le bug

Rouvrez le code de la classe TrafficLight et repérez, dans la fonction next(), le commentaire // This is intentionally wrong!.

Nos tests unitaires montrent que cette fonction ne repasse pas correctement au green. Le code vérifie actuellement si la valeur de lightIndex dépasse le nombre de couleurs du feu, mais l’index commence à 0. Il faut plutôt repasser au début dès que la valeur atteint le nombre exact de couleurs. Modifions le code pour réinitialiser lightIndex à 0 lorsqu’il est égal à la longueur de la liste des couleurs du feu :

// This is intentionally wrong!
if( this.lightIndex === TrafficLight.colors.length ) {
    this.lightIndex = 0;
}

Tous vos tests unitaires devraient maintenant réussir. Et le plus intéressant, c’est que même si la classe TrafficLight est remaniée ou considérablement modifiée, nos tests unitaires détecteront ce bug avant qu’il n’atteigne les utilisateurs.

Sortie du terminal montrant les tests unitaires Mocha pour les couleurs de TrafficLight, avec quatre tests réussis.

Pour conclure

Les tests unitaires sont faciles à mettre en place et constituent un outil efficace pour le développement logiciel. Ils aident à éliminer les bugs rapidement et à éviter qu’ils ne réapparaissent. Les projets restent ainsi plus faciles à gérer et à maintenir, même lorsqu’ils gagnent en taille et en complexité — en particulier au sein des grandes équipes de développement. Ces tests automatisés permettent également aux développeurs de remanier et d’optimiser leur code sans craindre que le nouveau code ne fonctionne pas correctement.

Les tests unitaires sont essentiels à votre processus de développement et vous aident à créer de meilleures applications JavaScript plus sécurisées.

Bonnes vérifications !

Lancez-vous dans les compétitions Capture The Flag

Apprenez à résoudre des défis Capture The Flag en regardant à la demande notre atelier virtuel d’initiation.

Lire la suite

feature insights context
Blog

Les attaques autonomes sont déjà là. La défense doit suivre leur rythme.

Les attaquants autonomes réduisent la fenêtre de défense. Découvrez comment la découverte, la correction, la validation et la prévention continues peuvent aider les équipes de sécurité à suivre le rythme.

illustration hero ai
Blog

L’ouragan de l’IA est là

L’IA accélère à la fois la création de logiciels et les cyberattaques. Les dirigeants doivent sécuriser les agents et le code dès leur conception, appliquer des contrôles à l’exécution et valider les défenses de manière indépendante.

feature insights context
Blog

La prévention est-elle essentiellement un problème résolu ?

La prévention dans le code généré par les agents est résolue sur le plan architectural, mais le défi reste de choisir des contrôles qui protègent la sécurité sans ralentir le développement.