Une introduction aux tests unitaires Jasmine

Jasmine est la bibliothèque JS la plus populaire pour les applications Web de test unitaire. Dans ce tutoriel, conçu pour les débutants, nous vous présenterons un guide rapide et complet pour tester avec Jasmine.

Vous découvrirez Jasmine, un framework de test comportemental populaire pour JavaScript. Nous verrons également un exemple pratique simple sur la façon d’écrire des tests unitaires avec Jasmine qui peut vous aider à vérifier facilement les bogues dans votre code.

En résumé, nous verrons comment écrire des suites de tests, des spécifications et des attentes et comment appliquer des matchers Jasmine intégrés ou créer vos propres matchers personnalisés

Nous verrons également comment vous pouvez regrouper des suites dans le but d’organiser vos tests pour des bases de code plus complexes.

Présentation de Jasmine

Jasmine est un framework de développement basé sur le comportement JavaScript très populaire (dans BDD, vous écrivez des tests avant d’écrire du code réel) pour les applications JavaScript de test unitaire. Il fournit des utilitaires qui peuvent être utilisés pour exécuter des tests automatisés pour le code synchrone et asynchrone.

Jasmine a de nombreuses fonctionnalités telles que:

  • Il est rapide et a une faible surcharge et aucune dépendance externe.
  • C’est une bibliothèque de piles incluses et offre tout ce dont vous avez besoin pour tester votre code.
  • Il est disponible à la fois pour Node et pour le navigateur.
  • Il peut être utilisé avec d’autres langages comme Python et Ruby.
  • Il ne nécessite pas le DOM.
  • Il fournit une syntaxe propre et facile à comprendre ainsi qu’une API riche et simple.
  • Nous pouvons utiliser un langage naturel pour décrire les tests et les résultats attendus.

Jasmine est un outil open source disponible sous licence MIT permissive. Au moment d’écrire ces lignes, la dernière version majeure est Jasmine 3.0 qui fournit de nouvelles fonctionnalités et quelques changements de rupture. La version 2.99 de Jasmine fournira différents avertissements de dépréciation pour les suites ayant un comportement différent dans la version 3.0, ce qui facilitera la migration des développeurs vers la nouvelle version.

Vous pouvez lire les nouvelles fonctionnalités et les modifications de rupture de ce document.

En utilisant Jasmine

Vous pouvez utiliser Jasmine de différentes manières:

  • à l’ancienne en incluant à la fois le noyau Jasmine et vos fichiers de test en utilisant une balise <script >,
  • comme outil CLI utilisant Node.js,
  • en tant que bibliothèque dans Node.js,
  • dans le cadre d’un système de construction comme Gulp.js ou Grognement.js via grunt-contrib-jasmine et gulp-jasmine-browser

Vous pouvez également utiliser Jasmine pour tester votre code Python avec jasmine-py qui peut être installé à partir de PyPI en utilisant la commande pip install jasmine. Ce paquet contient à la fois un serveur Web qui sert et exécute une suite Jasmine pour votre projet et un script CLI pour exécuter des tests et des intégrations continues.

Jasmine est également disponible pour les projets Ruby via jasmine-gem qui peut être installé en ajoutant gem 'jasmine' à votre fichier Gemmet en exécutant bundle install. Il comprend un serveur pour servir et exécuter des tests, un script CLI et également des générateurs pour les projets Ruby on Rails.

Concentrons-nous maintenant sur l’utilisation de Jasmine avec JavaScript:

Utilisation de Jasmine autonome

Commencez par télécharger la dernière version de Jasmine à partir de la page des versions.

Ensuite, extrayez simplement le fichier zip, de préférence dans un dossier du projet que vous souhaitez tester.

Le dossier contiendra un tas de fichiers et dossiers par défaut:

/src: contient les fichiers source que vous souhaitez tester. Cela peut être supprimé si vous avez déjà configuré le dossier de votre projet ou peut également être utilisé le cas échéant pour héberger votre code source.

/lib: contient les fichiers de base de Jasmine.

/spec: contient les tests que vous allez écrire.

SpecRunner.html: ce fichier est utilisé comme coureur de test. Vous exécutez vos spécifications en lançant simplement ce fichier.

C’est le contenu d’un fichier SpecRunner.html par défaut:

<!DOCTYPE html><html><head> <meta charset="utf-8"> <title>Jasmine Spec Runner v3.2.1</title> <link rel="shortcut icon" type="image/png" href="lib/jasmine-3.2.1/jasmine_favicon.png"> <link rel="stylesheet" href="lib/jasmine-3.2.1/jasmine.css"> <script src="lib/jasmine-3.2.1/jasmine.js"></script> <script src="lib/jasmine-3.2.1/jasmine-html.js"></script> <script src="lib/jasmine-3.2.1/boot.js"></script> <!-- include source files here... --> <script src="src/Player.js"></script> <script src="src/Song.js"></script> <!-- include spec files here... --> <script src="spec/SpecHelper.js"></script> <script src="spec/PlayerSpec.js"></script></head><body></body></html>

N’oubliez pas que vous devez modifier les fichiers inclus à partir des dossiers /src et /spec pour contenir vos fichiers source et de test réels.

Utilisation de Jasmine comme bibliothèque

Vous pouvez également utiliser Jasmine comme bibliothèque dans votre projet. Par exemple, le code suivant importe et exécute Jasmine:

var Jasmine = require('jasmine');var jasmine = new Jasmine();jasmine.loadConfigFile('spec/support/jasmine.json');jasmine.execute();

D’abord, nous demandons /importons Jasmine et nous utilisons la méthode loadConfigFile() pour charger le fichier de configuration disponible à partir du chemin spec/support/jasmine.json puis enfin nous exécutons Jasmine.

En utilisant Jasmine via la CLI

Vous pouvez également utiliser Jasmine depuis la CLI qui vous permet d’exécuter facilement des tests Jasmine et par défaut de sortir les résultats dans le terminal.

Nous allons suivre cette approche pour exécuter nos exemples de tests dans ce guide, alors lancez d’abord la commande suivante pour installer Jasmine globalement :

npm install -g jasmine

Vous devrez peut-être exécuter sudo pour installer les paquets npm globalement en fonction de votre configuration npm.

Maintenant, créez un dossier pour votre projet et naviguez à l’intérieur:

$ mkdir jasmine-project $ cd jasmine-project

Ensuite, exécutez la commande suivante pour initialiser votre projet pour Jasmine:

Cette commande crée simplement un dossier spec et un fichier de configuration JSON. Ceci est la sortie de la commande dir :

.└── spec └── support └── jasmine.json2 directories, 1 file

C’est le contenu d’un fichier jasmine.json :

{ "spec_dir": "spec", "spec_files": pec.js" ], "helpers": , "stopSpecOnExpectationFailure": false, "random": true}
  • spec_dir : spécifie où Jasmine recherche les fichiers de test.
  • spec_files: spécifie les modèles des fichiers de test, par défaut tous les fichiers JS qui se terminent par des chaînes Spec ou Spec.
  • helpers : spécifie où Jasmine recherche les fichiers d’assistance. Les fichiers d’assistance sont exécutés avant les spécifications et peuvent être utilisés pour définir des matchers personnalisés.
  • stopSpecOnExpectationFailure: une fois définie sur true arrêtera immédiatement une spécification lors de la première défaillance d’une attente (peut être utilisée comme option CLI via --stop-on-failure).
  • random: lorsqu’il est défini sur true, Jasmine exécutera de manière pseudo-aléatoire les cas de test (peut être utilisé comme option CLI via --random).

Les tableaux spec_files et helpers peuvent également contenir des motifs Glob (grâce au package node-glob) pour spécifier des chemins de fichiers qui sont des motifs que vous utilisez habituellement pour spécifier un ensemble de fichiers lorsque vous travaillez dans Bash (par exemple ls *.js).

Si vous n’utilisez pas l’emplacement par défaut du fichier de configuration jasmine.json, il vous suffit de spécifier l’emplacement personnalisé via l’option jasmine --config.

Vous pouvez trouver plus d’options CLI dans les documents officiels.

Comprendre le jasmin

Dans cette section, nous allons en apprendre davantage sur les éléments de base des tests de Jasmin tels que les suites, les spécifications, les attentes, les matchers et les espions, etc.

Dans le dossier de votre projet, exécutez la commande suivante pour initialiser un nouveau module de nœud :

Cela créera un fichier package.json avec les informations par défaut:

{ "name": "jasmine-project", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "keywords": , "author": "", "license": "ISC"}

Ensuite, créez un fichier index.js et ajoutez le code suivant :

function fibonacci(n){ if (n === 1) { return ; } else { var s = fibonacci(n - 1); s.push(s + s); return s; }}function isPrime(num){ for (let i = 2; i < num; i++) if (num % i === 0) return false; return num !== 1 && num !== 0;}function isEven(n) { return n % 2 == 0;}function isOdd(n) { return Math.abs(n % 2) == 1;}function toLowerCase(str){ return str.toLowerCase();}function toUpperCase(str){ return str.toUpperCase();}function contains(str, substring, fromIndex){ return str.indexOf(substring, fromIndex) !== -1;}function repeat(str, n){ return (new Array(n + 1)).join(str);}module.exports = { fibonacci: fibonacci, isPrime: isPrime, isEven: isEven, isOdd: isOdd, toLowerCase: toLowerCase, toUpperCase: toUpperCase, contains: contains, repeat: repeat};

Suites

Une suite regroupe un ensemble de spécifications ou de cas de test. Il est utilisé pour tester un comportement spécifique du code JavaScript qui est généralement encapsulé par un objet /classe ou une fonction. Il est créé à l’aide de la fonction globale Jasmine describe() qui prend deux paramètres, le titre de la suite de tests et une fonction qui implémente le code réel de la suite de tests.

Commençons par créer notre première suite de tests. Dans le dossier spec créez un fichier MyJSUtilitiesSpec.js et ajoutez:

describe("MyJSUtilities", function() { /* ... */ });

MyJSUtilities est le nom de cette suite de tests de niveau supérieur.

Comment regrouper et imbriquer les suites

Pour mieux organiser et décrire avec précision notre ensemble de tests, nous pouvons imbriquer les suites dans la suite de niveau supérieur. Par exemple, ajoutons deux suites à la suite MyJSUtilities:

describe("String Utils", function() { /*...*/});describe("Math Utils", function() { /*...*/});

Dans la suite des Utils mathématiques, ajoutons également deux suites imbriquées:

describe("Basic Math Utils", function() { /* ... */ }); describe("Advanced Math Utils", function() { /* ... */ });

Nous regroupons les tests associés en tests pour les Utils de chaîne, les Utils Mathématiques de base et les Utils Mathématiques avancés et les imbriquons dans le test de niveau supérieur suite MyJSUtilities. Cela composera vos spécifications sous forme d’arbres similaires à une structure de dossiers.

La structure d’imbrication sera affichée sur le rapport, ce qui vous permettra de trouver facilement les tests défaillants.

Comment exclure les suites

Vous pouvez désactiver temporairement une suite à l’aide de la fonction xdescribe(). Il a la même signature (paramètres) qu’une fonction describe(), ce qui signifie que vous pouvez rapidement désactiver vos suites existantes en ajoutant simplement un x à la fonction.

Les spécifications d’une fonction xdescribe() seront marquées en attente et ne seront pas exécutées dans le rapport.

Spécifications

Une spécification déclare un scénario de test qui appartient à une suite de tests. Cela se fait en appelant la fonction globale Jasmine it() qui prend deux paramètres, le titre de la spécification (qui décrit la logique que nous voulons tester) et une fonction qui implémente le cas de test réel.

Une spécification peut contenir une ou plusieurs attentes. Chaque attente est simplement une assertion qui peut renvoyer soit true ou false. Pour que la spécification soit transmise, toutes les attentes appartenant à la spécification doivent être true sinon la spécification échoue.

Dans notre suite d’Utils de chaînes, ajoutez ces spécifications :

describe("String Utils", function() { it("should be able to lower case a string",function() { /*...*/ }); it("should be able to upper case a string",function() { /*...*/ }); it("should be able to confirm if a string contains a substring",function() { /*...*/ }); it("should be able repeat a string multiple times",function() { /*...*/ });});

Dans notre suite d’Utils Mathématiques de base, ajoutons quelques spécifications :

describe("Basic Math Utils", function() { it("should be able to tell if a number is even",function() { /*...*/ }); it("should be able to tell if a number is odd",function() { /*...*/ }); });

Pour les Utils Mathématiques avancés, ajoutons les spécifications:

describe("Advanced Math Utils", function() { it("should be able to tell if a number is prime",function() { /*...*/ }); it("should be able to calculate the fibonacci of a number",function() { /*...*/ }); });

Comment exclure les spécifications

Tout comme les suites, vous pouvez également exclure des spécifications individuelles en utilisant la fonction xit() qui désactive temporairement la spécification it() et marque la spécification comme en attente.

Attentes

Les attentes sont créées à l’aide de la fonction expect() qui prend une valeur appelée réelle (cela peut être des valeurs, des expressions, des variables, des fonctions ou des objets, etc.). Les attentes composent la spécification et sont utilisées avec les fonctions d’appariement (via un chaînage) pour définir ce que le développeur attend d’une unité de code spécifique à exécuter.

Une fonction de matcher compare entre une valeur réelle (transmise à la fonction expect() avec laquelle elle est chaînée) et une valeur attendue (directement transmise en paramètre au matcher) et renvoie true ou false qui passe ou échoue la spécification.

Vous pouvez chaîner la fonction expect() avec plusieurs matchers. Pour annuler / inverser le résultat booléen de n’importe quel matcher, vous pouvez utiliser le mot-clé not avant d’appeler le matcher.

Implémentons les spécifications de notre exemple. Pour l’instant, nous allons utiliser le matcher expect() avec le matcher nothing() qui fait partie des matchers intégrés que nous verrons un peu plus tard. Cela passera toutes les spécifications car nous n’attendons rien à ce stade.

describe("MyJSUtilities", function() {describe(">String Utils", function() { it("should be able to lower case a string",function() { expect().nothing(); }); it("should be able to upper case a string",function() { expect().nothing(); }); it("should be able to confirm if a string contains a substring",function() { expect().nothing(); }); it("should be able repeat a string multiple times",function() { expect().nothing(); }); });describe("Math Utils", function() { describe("Basic Math Utils", function() { it("should be able to tell if a number is even",function() { expect().nothing(); }); it("should be able to tell if a number is odd",function() { expect().nothing(); }); }); describe("Advanced Math Utils", function() { it("should be able to tell if a number is prime",function() { expect().nothing(); }); it("should be able to calculate the fibonacci of a number",function() { expect().nothing(); }); }); });});

Ceci est une capture d’écran des résultats à ce stade:

Nous avons huit spécifications réussies et zéro échec.

Vous pouvez utiliser des matchers intégrés ou créer vos propres matchers personnalisés pour vos besoins spécifiques.

Matchers intégrés

Jasmine fournit un riche ensemble de matchers intégrés. Voyons quelques-uns des plus importants:

  • toBe()pour tester l’identité,
  • toBeNull()pour tester null,
  • toBeUndefined()/toBeDefined() pour tester pour undefined/pas undefined,
  • toBeNaN()pour tester NaN (Pas un nombre)
  • toEqual() pour tester l’égalité,
  • toBeFalsy()/toBeTruthy()pour tester la fausseté / véracité, etc.

Vous pouvez trouver la liste complète des matchers dans les documents.

Implémentons maintenant nos spécifications avec certains de ces matchers le cas échéant. Importez d’abord les fonctions que nous testons dans notre fichier MyJSUtilitiesSpec.js:

const utils = require("../index.js");

Ensuite, commencez par la suite Utils String et modifiez expect().nothing() avec les attentes appropriées.

Par exemple pour la première spécification, nous nous attendons à ce que la méthode toLowerCase() soit d’abord définie et renvoie ensuite une chaîne minuscule, c’est-à-dire :

it("should be able to lower case a string",function() { expect(utils.toLowerCase).toBeDefined(); expect(utils.toLowerCase("HELLO WORLD")).toEqual("hello world"); });

C’est le code complet de la suite:

describe(">String Utils", function() { it("should be able to lower case a string",function() { expect(utils.toLowerCase).toBeDefined(); expect(utils.toLowerCase("HELLO WORLD")).toEqual("hello world"); }); it("should be able to upper case a string",function() { expect(utils.toUpperCase).toBeDefined(); expect(utils.toUpperCase("hello world")).toEqual("HELLO WORLD"); }); it("should be able to confirm if a string contains a substring",function() { expect(utils.contains).toBeDefined(); expect(utils.contains("hello world","hello",0)).toBeTruthy(); }); it("should be able repeat a string multiple times",function() { expect(utils.repeat).toBeDefined(); expect(utils.repeat("hello", 3)).toEqual("hellohellohello"); }); });

Matchers personnalisés

Jasmine offre la possibilité d’écrire des matchers personnalisés pour implémenter des assertions non couvertes par les matchers intégrés ou simplement pour rendre les tests plus descriptifs et lisibles.

Par exemple, prenons la spécification suivante:

it("should be able to tell if a number is even",function() { expect(utils.isEven).toBeDefined(); expect(utils.isEven(2)).toBeTruthy(); expect(utils.isEven(1)).toBeFalsy(); });

Supposons que la méthode isEven() n’est pas implémentée. Si nous exécutons les tests, nous obtiendrons des messages comme la capture d’écran suivante:

Le message d’échec que nous recevons indique que l’indéfini attendu doit être défini, ce qui ne nous donne aucune idée de ce qui se passe. Rendons donc ce message plus significatif dans le contexte de notre domaine de code (cela sera plus utile pour les bases de code complexes). Pour cela, créons un matcher personnalisé.

Nous créons des matchers personnalisés en utilisant la méthode addMatchers() qui prend un objet composé d’une ou plusieurs propriétés qui seront ajoutées en tant que matchers. Chaque propriété doit fournir une fonction d’usine qui prend deux paramètres : util, qui a un ensemble de fonctions utilitaires à utiliser par les matchers (voir : matchersUtil.js) et customEqualityTesters qui doit être transmis si util.equals est appelé et doit renvoyer un objet avec une fonction compare qui sera appelée pour vérifier l’attente.

Nous devons enregistrer le matcher personnalisé avant d’exécuter chaque spécification en utilisant la méthode beforeEach():

describe("/Basic Math Utils", function () {beforeEach(function () {jasmine.addMatchers({hasEvenMethod: function (util, customEqualityTesters) {return {compare: function (actual, expected) {var result = { pass: utils.isEven !== undefined };if (result.pass) {result.message = "Expected isEven() to be not defined."}else {result.message = "Expected isEven() to be defined."}return result;}}}});});/*...*/});

Nous pouvons alors utiliser le matcher personnalisé au lieu de expect(utils.isEven).toBeDefined():

expect().hasEvenMethod();

Cela nous donnera un meilleur message d’échec:

En utilisant beforeEach() et afterEach()

Pour initialiser et nettoyer vos spécifications, Jasmine fournit deux fonctions globales, beforeEach() et afterEach() :

  • La fonction beforeEach est appelée une fois avant chaque spécification dans la suite où elle est appelée.
  • La fonction afterEach est appelée une fois après chaque spécification dans la suite où elle est appelée.

Par exemple, si vous devez utiliser des variables dans votre suite de tests, vous pouvez simplement les déclarer au début de la fonction describe() et placer n’importe quel code d’initialisation ou d’instanciation dans une fonction beforeEach(). Enfin, vous pouvez utiliser la fonction afterEach() pour réinitialiser les variables après chaque spécification afin que vous puissiez avoir des tests unitaires purs sans avoir besoin de répéter le code d’initialisation et de nettoyage pour chaque spécification.

La fonction beforeEach() est également parfaitement combinée avec de nombreuses API Jasmine telles que la méthode addMatchers() pour créer des matchers personnalisés ou également avec la fonction done() pour attendre des opérations asynchrones avant de continuer les tests.

Échec d’un test

Vous pouvez forcer un test à échouer en utilisant la méthode globale fail() disponible dans Jasmine. Par exemple :

it("should explicitly fail", function () { fail('Forced to fail'); });

Vous devriez obtenir l’erreur suivante:

Test des exceptions

Lorsque vous testez votre code à l’unité, des erreurs et des exceptions peuvent être générées, vous devrez peut-être tester ces scénarios. Jasmine fournit les matrices toThrow() et toThrowError() pour tester lorsqu’une exception est levée ou pour tester une exception spécifique, respectivement.

Par exemple si nous avons une fonction qui lève une exception TypeError:

function throwsError() { throw new TypeError("A type error"); }

Vous pouvez écrire une spécification pour tester si une exception est levée:

it('it should throw an exception', function () { expect(throwsError).toThrow(); });

Ou vous pouvez également utiliser test pour l’exception TypeError :

it('it should throw a TypeError', function () { expect(throwsError).toThrowError(TypeError); });

Comprendre les espions

Le plus souvent, les méthodes dépendent d’autres méthodes. Cela signifie que lorsque vous testez une méthode, vous pouvez également tester ses dépendances. Ceci n’est pas recommandé lors des tests, c’est-à-dire que vous devez vous assurer de tester la fonction pure en isolant la méthode et en voyant comment elle se comporte compte tenu d’un ensemble d’entrées.

Jasmine fournit des espions qui peuvent être utilisés pour espionner / écouter les appels de méthode sur des objets et signaler si une méthode est appelée et avec quel contexte et quels arguments.

Jasmine fournit deux façons d’espionner les appels de méthode : en utilisant les méthodes spyOn() ou les méthodes createSpy().

Vous pouvez utiliser spyOn()lorsque la méthode existe déjà sur l’objet, sinon vous devez utiliser jasmine.createSpy() qui renvoie une nouvelle fonction.

Par défaut, un espion ne signalera que si un appel a été effectué sans appeler via la fonction espionnée (i.e la fonction cessera de s’exécuter), mais vous pouvez modifier le comportement par défaut en utilisant ces méthodes :

  • and.callThrough(): appel via la fonction d’origine,
  • and.returnValue(value): renvoie la valeur spécifiée,
  • and.callFake(fn): appelle la valeur spécifiée,
  • and.callFake(fn): appelle fonction fausse au lieu de la fonction d’origine,
  • and.throwError(err): génère une erreur,
  • and.stub(): réinitialise le comportement de blocage par défaut.

Vous pouvez utiliser un espion pour collecter des statistiques d’exécution sur la fonction espionnée, par exemple si vous voulez savoir combien de fois votre fonction a été appelée.

Disons que nous voulons nous assurer que notre méthode toUpperCase() utilise la méthode String.toUpperCase() intégrée, nous devons simplement espionner String.toUpperCase() en utilisant:

it("should be able to upper case a string", function () { 
var spytoUpperCase = spyOn(String.prototype, 'toUpperCase') 
expect(utils.toUpperCase).toBeDefined(); expect(utils.toUpperCase("hello world")).toEqual("HELLO WORLD"); expect(String.prototype.toUpperCase).toHaveBeenCalled(); expect(spytoUpperCase.calls.count()).toEqual(1); });

Le test a échoué en raison de la deuxième attente car utils.toUpperCase("hello world") a renvoyé indéfini au lieu du HELLO WORLD attendu. En effet, comme nous l’avons mentionné plus tôt après la création de l’espion sur toUpperCase(), la méthode n’est pas exécutée. Nous devons modifier ce comportement par défaut en appelant callThrough():

Veuillez noter qu’une fonction spy remplace la fonction espionnée par un talon par défaut. Si vous devez appeler la fonction d’origine à la place, vous pouvez ajouter .and.callThrough() à votre objet spy

var spytoUpperCase = spyOn(String.prototype, 'toUpperCase').and.callThrough();

Maintenant toutes les attentes passent.

Vous pouvez également utiliser and.callFake() ou and.returnValue() pour simuler la fonction espionnée ou simplement la valeur de retour si vous n’appelez pas via la fonction réelle:

var spytoUpperCase = spyOn(String.prototype, 'toUpperCase').and.returnValue("HELLO WORLD"); 
var spytoUpperCase = spyOn(String.prototype, 'toUpperCase').and.callFake(function(){ return "HELLO WORLD"; });

Maintenant, si nous n’utilisons pas l’implémentation intégrée String.toUpperCase()dans notre propre implémentation utils.toUpperCase(), nous obtiendrons ces échecs:

Les deux attentes expect(String.prototype.toUpperCase).toHaveBeenCalled()expect(spytoUpperCase.calls.count()).toEqual(1) ont échoué.

Comment gérer l’asynchronicité dans Jasmine

Si le code que vous testez contient des opérations asynchrones, vous avez besoin d’un moyen d’informer Jasmine lorsque les opérations asynchrones sont terminées.

Par défaut, Jasmine attend que toute opération asynchrone, définie par un rappel, une promesse ou le mot clé async, soit terminée. Si Jasmine trouve un mot-clé callback, promise ou asynchrone dans l’une de ces fonctions : beforeEachafterEachbeforeAllafterAll, et it il attendra que l’asynchrone soit effectué avant de passer à l’opération suivante.

En utilisant done() avec beforeEach()/it()..

Prenons notre exemple simulateAsyncOp() qui simule une opération asynchrone en utilisant setTimeout(). Dans un scénario réel, cela peut être une requête Ajax ou toute chose similaire qui se produit de manière asynchrone :

function simulateAsyncOp(callback){ 
setTimeout(function () { callback(); }, 2000); }

Pour tester cette fonction, nous pouvons utiliser la fonction beforeEach() avec la fonction spéciale done() rappel. Notre code doit appeler done() pour indiquer à Jasmine que l’opération asynchrone est terminée:

describe("/Async Op", function () {var asyncOpCompleted = false;beforeEach(function (done) {utils.simulateAsyncOp(function(){ asyncOpCompleted = true; done();});});it("should be able to tell if the async call has completed", function () { expect(asyncOpCompleted).toEqual(true);});});

Nous pouvons rapidement remarquer un inconvénient de cette méthode, nous devons donc écrire notre code pour accepter le rappel done(). Dans notre cas, nous n’avons pas codé en dur la méthode done() dans notre simulateAsyncOp(fn) mais nous avons fourni un paramètre de rappel juste pour pouvoir appeler done().

En utilisant des promesses

Si vous ne souhaitez pas créer de code qui dépend de la façon dont vous écrivez votre test, vous pouvez utiliser une promesse à la place et appeler le rappel done() lorsque la promesse est résolue. Ou mieux encore, dans Jasmine 2.7+, si votre code renvoie un Promise, Jasmine attendra qu’il soit résolu ou rejeté avant d’exécuter le code suivant.

En utilisant async/await

Jasmine 2.7+ prend en charge les appels async et await dans les spécifications. Cela vous évite de placer des assertions dans un bloc .then() ou .catch().

it("should work with async/await", async () => { let completed = false; completed = await utils.simulateAsyncOp(); expect(completed).toEqual(true); });

Ceci est l’implémentation de simulateAsyncOp:

function simulateAsyncOp() { 
return new Promise(resolve => { setTimeout(() => { resolve(true); }, 1000); }); }

Utilisation de l’horloge Jasmine

L’horloge Jasmine est utilisée pour tester le code asynchrone qui dépend de fonctions temporelles telles que setTimeout() de la même manière que nous testons le code synchrone en se moquant des API basées sur le temps avec des méthodes personnalisées. De cette façon, vous pouvez exécuter les fonctions testées de manière synchrone en contrôlant ou en avançant manuellement l’horloge.

Vous pouvez installer l’horloge Jasmine en appelant la fonction jasmine.clock().install dans votre spécification ou suite.

Après avoir utilisé l’horloge, vous devez la désinstaller pour restaurer les fonctions d’origine.

Avec Jasmine clock, vous pouvez contrôler les fonctions JavaScript setTimeout ou setInterval en cochant l’horloge pour avancer dans le temps en utilisant la fonction jasmine.clock().tick, qui prend le nombre de millisecondes avec lesquelles vous pouvez vous déplacer.

Vous pouvez également utiliser l’horloge Jasmine pour simuler la date actuelle.

beforeEach(function () {jasmine.clock().install();});afterEach(function() {jasmine.clock().uninstall();});it("should call the asynchronous operation synchronously", function() {var completed = false;utils.simulateAsyncOp(function(){completed = true;});expect(completed).toEqual(false);jasmine.clock().tick(1001);expect(completed).toEqual(true);});

Il s’agit de la fonction simulateAsyncOp:

function simulateAsyncOp(callback){ 
setTimeout(function () { callback(); }, 1000); }

Si vous n’avez pas spécifié d’heure pour la fonction mockDate, elle utilisera la date actuelle.

Gestion des erreurs

Si votre code asynchrone échoue en raison d’une erreur, vous souhaitez que vos spécifications échouent correctement. À partir de Jasmine 2.6+, toutes les erreurs non gérées sont envoyées à la spécification actuellement exécutée.

Jasmine fournit également un moyen que vous pouvez utiliser si vous devez explicitement échouer vos spécifications:

  • en utilisant le rappel done() avec la méthode beforeEach() en appelant la méthode done.fail(err),
  • en transmettant simplement une erreur au rappel done(err) (Jasmine 3+),
  • appelant la méthode reject() d’un Promise.

Conclusion

Dans ce guide, nous avons présenté Jasmine et vu comment commencer à utiliser Jasmine pour tester à l’unité votre code JavaScript. Merci d’avoir lu!

Cet article a été publié à l’origine dans techiediaries.

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *