Test dans Node.js

Un cycle de développement piloté par les tests simplifie le processus de rédaction du code, le rend plus facile et plus rapide à long terme. Mais écrire des tests ne suffit pas en soi, il est essentiel de connaître le type de tests à écrire et comment structurer le code pour se conformer à ce modèle. Dans cet article, nous allons voir comment créer une petite application dans Node.js en suivant un modèle TDD..

Outre les tests unitaires simples, que nous connaissons tous; Nous pouvons également avoir le code Async de Node.js en cours d’exécution, ce qui ajoute un extra dimension en ce sens que nous ne connaissons pas toujours l'ordre dans lequel les fonctions seront exécutées ou que nous essayons peut-être de tester quelque chose dans un rappel ou de vérifier le fonctionnement d'une fonction asynchrone.

Dans cet article, nous allons créer une application Node permettant de rechercher des fichiers correspondant à une requête donnée. Je sais qu'il y a déjà des choses pour ça (ack) mais dans l’intérêt de démontrer le TDD, je pense que cela pourrait être un projet complet.

La première étape consiste évidemment à écrire des tests, mais même avant cela, nous devons choisir un framework de test. Vous pouvez utiliser le nœud vanille, car il existe un affirmer bibliothèque intégrée, mais ce n'est pas beaucoup en termes de coureur de test, et est à peu près l'essentiel.

Une autre option et probablement mon préféré pour une utilisation générale est Jasmine. C'est assez autonome, vous n'avez aucune autre dépendance à ajouter à vos scripts et la syntaxe est très propre et facile à lire. La seule raison pour laquelle je ne vais pas utiliser cela aujourd'hui, c'est parce que je pense que Jack Franklin a fait un excellent travail dans cette récente série de Tuts +, et il est bon de connaître vos options pour pouvoir choisir le meilleur outil pour votre situation.


Ce que nous allons construire

Dans cet article, nous utiliserons le coureur de test 'Mocha' flexible ainsi que la bibliothèque d'assertions Chai..

Contrairement à Jasmine, qui ressemble davantage à une suite de tests complète dans un seul paquet, Mocha ne s'occupe que de la structure globale mais n'a rien à voir avec les affirmations réelles. Cela vous permet de conserver une apparence cohérente lors de l'exécution de vos tests, mais également de lancer la bibliothèque d'assertions la mieux adaptée à votre situation..

Ainsi, par exemple, si vous utilisiez la bibliothèque 'assert' de vanilla, vous pouvez l'associer à Mocha pour ajouter une structure à vos tests..

Le chai est une option assez populaire et concerne également les options et la modularité. Même en l'absence de plug-ins, trois interfaces différentes peuvent être utilisées à l'aide de l'API par défaut, selon que vous souhaitez utiliser un style TDD plus classique ou une syntaxe BDD plus détaillée..

Alors maintenant que nous savons ce que nous allons utiliser, entrons dans l'installation.


La mise en place

Pour commencer, installons Mocha globalement en lançant:

npm installer -g moka

Lorsque cela est terminé, créez un nouveau dossier pour notre projet et exécutez-y les opérations suivantes:

npm installer chai

Cela installera une copie locale de Chai pour notre projet. Ensuite, créez un dossier nommé tester dans le répertoire de notre projet, car il s'agit de l'emplacement par défaut, Mocha recherchera des tests.

C’est à peu près cela pour la configuration, la prochaine étape consiste à expliquer comment structurer vos applications lorsque vous suivez un processus de développement piloté par des tests..


Structurer votre application

Il est important de savoir, en suivant une approche TDD, ce qui doit être testé et ce qui ne l’est pas. Une règle empirique est de ne pas écrire de tests pour le code des autres peuples déjà testés. Ce que je veux dire par ceci est le suivant: supposons que votre code ouvre un fichier, vous n'avez pas besoin de tester l'individu fs fonctionne, cela fait partie de la langue et est censé être déjà bien testé. Il en va de même lorsque vous utilisez des bibliothèques tierces, vous ne devez pas structurer des fonctions qui appellent principalement ces types de fonctions. Vous n'écrivez pas vraiment de tests pour ces tests et à cause de cela, vous avez des lacunes dans le cycle de TDD.

Maintenant, bien sûr, avec chaque style de programmation, il y a beaucoup d'opinions différentes et les gens auront des points de vue différents sur la façon de procéder. Mais l'approche que j'utilise consiste à créer des composants individuels à utiliser dans votre application, chacun résolvant un problème fonctionnel unique. Ces composants sont construits à l'aide de TDD, ce qui leur permet de fonctionner comme prévu et de ne pas endommager leur API. Ensuite, vous écrivez votre script principal, qui est essentiellement tout le code collé, et n’a pas besoin d’être testé / ne peut pas être testé, dans certaines situations.

Cela signifie également que la plupart de vos composants peuvent être réutilisés à l'avenir car ils n'ont pas vraiment grand-chose à faire, directement, avec le script principal..

Suite à ce que je viens de dire, il est courant de créer un dossier nommé 'lib'où vous mettez tous les composants individuels. Donc, jusqu'ici, vous devriez avoir installé Mocha et Chai, puis un répertoire de projet avec deux dossiers: 'lib' et 'tester'.


Débuter avec TDD

Juste au cas où vous êtes nouveau sur TDD, j’ai pensé que ce serait une bonne idée de couvrir rapidement le processus. La règle de base est que vous ne pouvez écrire aucun code sauf si le testeur vous le demande..

Essentiellement, vous écrivez ce que votre code est censé faire avant de le faire. Lorsque vous codez, vous avez un objectif très ciblé et vous ne compromettez jamais votre idée en vous écartant de la tête ou en pensant trop loin. De plus, étant donné que tous les codes de votre code seront associés à un test, vous pouvez être certain de ne jamais détruire votre application à l'avenir..

En réalité, un test est simplement une déclaration de ce qu'une fonction est censée faire lorsqu'elle est exécutée. Vous exécutez ensuite votre programme d'exécution de test, ce qui échouera évidemment (puisque vous n'avez pas encore écrit le code), puis vous écrivez le montant minimum. de code nécessaire pour réussir le test qui a échoué. Il est important de ne jamais sauter cette étape, car parfois, un test réussira avant même que vous ajoutiez du code, en raison du code que vous avez dans la même classe ou fonction. Lorsque cela se produit, vous avez soit écrit plus de code que ce que vous étiez censé faire pour un test différent ou il s'agit simplement d'un mauvais test (généralement pas assez spécifique)..

Toujours selon notre règle ci-dessus, si le test réussit immédiatement, vous ne pouvez écrire aucun code, car il ne vous l'a pas dit. En écrivant continuellement des tests puis en implémentant les fonctionnalités, vous construisez des modules solides sur lesquels vous pouvez compter..

Une fois que vous avez terminé d'implémenter et de tester votre composant, vous pouvez alors refactoriser le code pour l'optimiser et le nettoyer, mais assurez-vous que le refactoring n'échoue pas l'un des tests que vous avez en place et, plus important encore, ne le fait pas. ne pas ajouter de fonctionnalités non testées.

Chaque bibliothèque de test aura sa propre syntaxe, mais elles suivront généralement le même modèle: faire des assertions et ensuite vérifier si elles passent. Puisque nous utilisons Mocha et Chai, jetons un coup d’œil à leurs syntaxes commençant par Chai.


Moka & Chai

J'utiliserai la syntaxe BDD «Expect», car comme je l'ai mentionné, Chai est livré avec quelques options prêtes à l'emploi. Cette syntaxe fonctionne de la manière suivante: vous commencez par appeler la fonction expect, en lui transmettant l'objet sur lequel vous souhaitez effectuer une assertion, puis vous l'enchaînez avec un test spécifique. Voici un exemple de ce que je veux dire:

attendre (4 + 5) .équal (9);

C'est la syntaxe de base, nous disons attendons l'ajout de 4 et 5 égaler 9. Maintenant, ce n’est pas un bon test car le 4 et 5 sera ajouté par Node.js avant même que la fonction ne soit appelée, nous testons donc essentiellement mes compétences en mathématiques, mais j'espère que vous comprendrez l'idée générale. L'autre chose que vous devriez noter, c'est que cette syntaxe n'est pas très lisible, en termes de flux d'une phrase anglaise normale. Sachant cela, Chai a ajouté la chaîne suivante qui ne font rien, mais vous pouvez les ajouter pour la rendre plus détaillée et lisible. Les getters de chaîne sont les suivants:

  • à
  • être
  • été
  • est
  • cette
  • et
  • avoir
  • avec
  • à
  • de
  • même
  • une
  • un

En utilisant ce qui précède, nous pouvons réécrire notre test précédent comme suit:

attendre (4 + 5) .à.équivalent (9);

J'aime beaucoup la convivialité de l'ensemble de la bibliothèque, que vous pouvez vérifier dans leur API. Des choses simples comme annuler l'opération sont aussi simples qu'écrire .ne pas avant le test:

attendre (4 + 5) .à.pas.équivalent (10);

Donc, même si vous n'avez jamais utilisé la bibliothèque auparavant, il ne sera pas difficile de comprendre ce qu'un test tente de faire..

La dernière chose que je voudrais examiner avant de commencer notre premier test est la façon dont nous structurons notre code dans Mocha.

Moka

Mocha est le coureur de test, donc il ne se préoccupe pas vraiment des tests réels, mais de la structure des tests, car c'est ainsi qu'il sait ce qui échoue et comment structurer les résultats. La façon dont vous construisez, est-ce que vous créez plusieurs décrire blocs qui décrivent les différents composants de votre bibliothèque, puis vous ajoutez il blocs pour spécifier un test spécifique.

Pour un exemple rapide, disons que nous avions une classe JSON et que cette classe avait une fonction pour analyser JSON et que nous voulions nous assurer que la fonction d'analyse pouvait détecter une chaîne JSON mal formatée, nous pourrions la structurer comme suit:

describe ("JSON", function () describe (". parse ()", function () it ("devrait détecter les chaînes JSON mal formées", function () // le test va ici)))) ;

Ce n'est pas compliqué, et c'est une préférence personnelle d'environ 80%, mais si vous gardez ce type de format, les résultats du test devraient apparaître dans un format très lisible..

Nous sommes maintenant prêts à écrire notre première bibliothèque, commençons par un simple module synchrone, pour mieux nous familiariser avec le système. Notre application doit pouvoir accepter les options de ligne de commande pour définir des paramètres tels que le nombre de niveaux de dossiers dans lesquels notre application doit effectuer une recherche et la requête elle-même..

Pour gérer tout cela, nous allons créer un module qui accepte la chaîne de la commande et analyse toutes les options incluses ainsi que leurs valeurs..

Le module tag

Il s'agit d'un excellent exemple de module que vous pouvez réutiliser dans toutes vos applications en ligne de commande, car ce problème se pose souvent. Ce sera une version simplifiée d’un paquet que j’ai sous npm, appelé ClTags. Donc, pour commencer, créez un fichier nommé tags.js dans le dossier lib, puis un autre fichier nommé tagsSpec.js à l'intérieur du dossier de test.

Nous devons extraire la fonction Chai expect, car ce sera la syntaxe d'assertion que nous utiliserons et nous devons extraire le fichier de balises afin de pouvoir le tester. En résumé, avec une configuration initiale, cela devrait ressembler à ceci:

var expect = require ("chai"). expect; var tags = require ("… /lib/tags.js"); describe ("Tags", function () );

Si vous exécutez maintenant la commande 'mocha' à partir de la racine de notre projet, tout devrait passer comme prévu. Maintenant, réfléchissons à ce que notre module va faire; nous voulons lui transmettre le tableau d'arguments de commande utilisé pour exécuter l'application, puis nous souhaitons qu'il construise un objet avec toutes les balises. Ce serait bien si nous pouvions également lui transmettre un objet de paramètres par défaut. rien ne sera remplacé, nous aurons des réglages déjà stockés.

Lorsqu’il s’agit de balises, de nombreuses applications fournissent également des options de raccourcis qui ne sont qu’un seul caractère. Par conséquent, si nous voulions définir la profondeur de notre recherche, nous pourrions permettre à l’utilisateur de spécifier quelque chose comme: --profondeur = 2 ou quelque chose comme -d = 2 qui devrait avoir le même effet.

Commençons donc par les balises longues (par exemple, '--depth = 2'). Commençons par le premier test:

describe ("Tags", function () describe ("# parse ()", function () it ("devrait analyser les balises formées longtemps", function () var args = ["--depth = 4", " --hello = world "]; var results = tags.parse (args); expect (résultats) .to.have.a.property (" depth ", 4); expect (résultats) .to.have.a.property ("Bonjour le monde"); ); ); );

Nous avons ajouté une méthode à notre suite de tests appelée analyser et nous avons ajouté un test pour les tags formés longtemps. À l'intérieur de ce test, j'ai créé un exemple de commande et ajouté deux assertions pour les deux propriétés qu'il devrait capturer..

En exécutant Mocha maintenant, vous devriez avoir une erreur, à savoir que Mots clés n'a pas de analyser une fonction. Donc, pour corriger cette erreur, ajoutons un analyser fonction au module tags. Une façon assez typique de créer un module de noeud est la suivante:

exports = module.exports = ; exports.parse = function () 

L'erreur a dit que nous avions besoin d'un analyser méthode donc nous l'avons créé, nous n'avons ajouté aucun autre code à l'intérieur car il ne nous l'avait pas encore dit. En vous en tenant au strict minimum, vous êtes assuré de ne pas écrire plus que ce que vous êtes censé écrire et de vous retrouver avec un code non testé.

Lançons à nouveau Mocha. Cette fois, nous devrions avoir une erreur en nous disant qu'il ne peut pas lire une propriété nommée. profondeur à partir d'une variable non définie. C’est parce qu’actuellement notre analyser function ne retourne rien, ajoutons donc du code pour qu'il retourne un objet:

exports.parse = function () var options =  options de retour; 

Nous avançons lentement, si vous exécutez à nouveau Mocha, il ne devrait y avoir aucune exception, mais juste un message d'erreur indiquant que notre objet vide n'a pas de propriété appelée profondeur.


Nous pouvons maintenant entrer dans un code réel. Pour que notre fonction analyse la balise et l'ajoute à notre objet, nous devons parcourir le tableau d'arguments et supprimer les doubles tirets au début de la clé..

exports.parse = function (args) options var = = pour (var i dans arguments) // Parcourir les arguments var argument = arguments [i]; // Vérifie si l'étiquette formée depuis longtemps if (arg.substr (0, 2) === "-") arg = arg.substr (2); // Recherche le signe égal if (arg.indexOf ("=")! == -1) arg = arg.split ("="); clé var = arg.shift (); options [key] = arg.join ("=");  options de retour; 

Ce code parcourt la liste des arguments, garantit que nous traitons une balise longue, puis la divise en fonction du premier caractère égal à pour créer la paire clé et valeur de l'objet options..

Maintenant, cela résout presque notre problème, mais si nous exécutons à nouveau Mocha, vous verrez que nous avons maintenant une clé pour la profondeur, mais elle est définie sur une chaîne au lieu d'un nombre. Il est un peu plus facile de manipuler des nombres plus tard dans notre application. Le code à ajouter est donc de convertir les valeurs en chiffres autant que possible. Ceci peut être réalisé avec certains RegEx et le analyse fonctionner comme suit:

 if (arg.indexOf ("=")! == -1) arg = arg.split ("="); clé var = arg.shift (); var value = arg.join ("="); if (/^[0-9]+$/.test(value)) value = parseInt (value, 10);  options [clé] = valeur; 

En exécutant Mocha maintenant, vous devriez obtenir une passe avec un test. On peut soutenir que la conversion de nombre doit faire partie de son propre test, ou du moins être mentionnée dans la déclaration de tests, afin d'éviter par erreur de supprimer l'assertion de conversion de nombre; il suffit donc d'ajouter "ajouter et convertir des nombres" à la il déclaration pour cet essai ou séparez-le en un nouveau il bloc. Cela dépend vraiment si vous considérez ce "comportement par défaut évident" ou une fonctionnalité distincte.


Maintenant, comme j'ai essayé de souligner tout au long de cet article, quand vous voyez une spécification qui passe, il est temps d'écrire plus de tests. La prochaine chose que je voulais ajouter était le tableau par défaut, donc à l'intérieur du tagsSpec fichier ajoutons ce qui suit il bloquer juste après le précédent:

 it ("devrait analyser les balises formées longtemps et convertir les nombres", function () var args = ["--depth = 4", "--hello = world"]; var results = tags.parse (args); expect ( résultats) .à.avoir.une.propriété ("profondeur", 4); attendre (résultats) .à.avoir.une.propriété ("bonjour", "monde");); it ("devrait revenir aux valeurs par défaut", function () var args = ["--depth = 4", "--hello = world"]; var par défaut = profondeur: 2, foo: "bar"; var results = tags.parse (arguments, valeurs par défaut); var attendu = profondeur: 4, foo: "bar", bonjour: "monde"; expect (résultats) .to.deep.equal (attendu););

Nous utilisons ici un nouveau test, le deep equal qui convient pour faire correspondre deux objets à des valeurs égales. Alternativement, vous pouvez utiliser le eql test qui est un raccourci mais je pense que cela est plus clair. Ce test transmet deux arguments en tant que chaîne de commande et deux valeurs par défaut avec un chevauchement, afin que nous puissions avoir une bonne répartition des cas de test..

Lancer Mocha maintenant, vous devriez avoir une sorte de diff, contenant les différences entre ce qui est attendu et ce qu’il a réellement.


Continuons maintenant vers le tags.js module, et ajoutons cette fonctionnalité dans. C'est un correctif assez simple à ajouter, nous devons simplement accepter le deuxième paramètre, et quand il est défini sur un objet, nous pouvons remplacer l'objet vide standard au début par cet objet:

exports.parse = fonction (arguments, valeurs par défaut) var options = ; if (typeof par défaut === "objet" &&! (instance par défaut de Array)) options = par défaut

Cela nous ramènera à un état vert. La prochaine chose que je veux ajouter est la possibilité de spécifier simplement une balise sans valeur et de la laisser fonctionner comme un booléen. Par exemple, si nous venons de définir --searchContents ou quelque chose comme ça, il va juste ajouter que dans notre tableau d'options avec une valeur de vrai.

Le test pour ceci ressemblerait à ceci:

 it ("devrait accepter les balises sans valeur comme un booléen", function () var args = ["--searchContents"]; var résultats = tags.parse (args); expect (résultats) .to.ha..ve.property ("searchContents", true););

Cela nous donnera l'erreur suivante comme avant:


À l'intérieur de la pour boucle, quand nous avons obtenu une correspondance pour une longue balise formée, nous avons vérifié si elle contenait un signe égal; nous pouvons rapidement écrire le code pour ce test en ajoutant un autre clause à cette si déclaration et juste mettre la valeur à vrai:

 if (arg.indexOf ("=")! == -1) arg = arg.split ("="); clé var = arg.shift (); var value = arg.join ("="); if (/^[0-9]+$/.test(value)) value = parseInt (value, 10);  options [clé] = valeur;  else options [arg] = true; 

La prochaine chose que je veux ajouter, ce sont les substitutions des balises abrégées. Ce sera le troisième paramètre de la analyser fonction et sera fondamentalement un objet avec des lettres et leurs remplaçants correspondants. Voici les spécifications pour cet ajout:

 it ("devrait accepter les balises courtes", function () var args = ["-sd = 4", "-h"]; var remplacements = s: "searchContents", d: "profondeur", h: " hello "; var résultats = tags.parse (arguments, , remplacements); var attendu = searchContents: true, profondeur: 4, hello: true; expect (résultats) .to.deep.equal (attendu); );

Le problème avec les étiquettes de sténographie est qu'elles peuvent être combinées dans une rangée. Ce que je veux dire par là est différent des balises formées longues où chacune est séparée, avec des balises manuelles courtes - puisqu'elles ne sont chacune qu'une lettre longue - vous pouvez appeler trois différentes en tapant -vgh. Cela rend l’analyse un peu plus difficile car nous devons tout de même permettre à l’opérateur égaux d’ajouter une valeur à la dernière balise mentionnée, tout en enregistrant les autres balises. Mais ne vous inquiétez pas, ce n'est rien qui ne peut être résolu avec suffisamment de popping et de décalage.

Voici le correctif entier, depuis le début de la analyser une fonction:

exports.parse = fonction (arguments, valeurs par défaut, remplacements) var options = ; if (typeof par défaut === "objet" &&! (instance par défaut de Array)) options = par défaut if (typeof remplacements === "objet" &&!! (instance par défaut de Array)) pour (variable dans arguments)  var arg = args [i]; if (arg.charAt (0) === "-" && arg.charAt (1)! = "-") arg = arg.substr (1); if (arg.indexOf ("=")! == -1) arg = arg.split ("="); var keys = arg.shift (); var value = arg.join ("="); arg = keys.split (""); clé var = arg.pop (); if (remplacements.hasOwnProperty (clé)) clé = remplacements [clé];  args.push ("-" + clé + "=" + valeur);  else arg = arg.split ("");  arg.forEach (fonction (clé) if (remplacements.hasOwnProperty (clé)) clé = remplacements [clé]; args.push ("-" + clé);); 

C'est beaucoup de code (en comparaison), mais tout ce que nous faisons est de scinder l'argument en un signe égal, puis de scinder cette clé en lettres individuelles. Donc, par exemple si nous passions -gj = asd nous diviserions le asd dans une variable appelée valeur, et puis nous diviserions le gj section en caractères individuels. Le dernier personnage (j dans notre exemple) deviendra la clé de la valeur (asd) alors que toutes les autres lettres le précédant seront simplement ajoutées en tant que balises booléennes classiques. Je ne voulais pas simplement traiter ces balises maintenant, juste au cas où nous changerions la mise en œuvre plus tard. Nous ne faisons donc que convertir ces balises hand hand en une version longue, puis laisser notre script le gérer plus tard..

Courir à nouveau Mocha nous ramènera à nos illustres résultats verts de quatre tests réussis pour ce module.

Nous pouvons maintenant ajouter quelques éléments supplémentaires à ce module de balises pour le rapprocher du package npm, comme la possibilité de stocker également des arguments en texte brut pour des éléments tels que les commandes ou la possibilité de collecter tout le texte à la fin, par exemple. propriété de requête. Mais cet article commence déjà à être long et j'aimerais passer à la mise en œuvre de la fonctionnalité de recherche..


Le module de recherche

Nous venons de créer un module étape par étape en suivant une approche TDD et j'espère que vous avez eu l'idée et le sentiment de savoir comment écrire de la sorte. Mais pour que cet article continue de progresser, pour le reste de l'article, j'accélérerai le processus de test en regroupant les éléments et en vous montrant simplement les versions finales des tests. Il s’agit plus d’un guide sur les différentes situations qui peuvent survenir et sur la manière de rédiger des tests pour elles..

Il suffit donc de créer un fichier nommé search.js dans le dossier lib et un searchSpec.js fichier à l'intérieur du dossier de test.

Ensuite ouvrez le fichier de spécification et configurons notre premier test qui peut être pour que la fonction obtienne une liste de fichiers basée sur un fichier. profondeur paramètre, c’est aussi un excellent exemple pour les tests qui nécessitent un peu de configuration externe pour fonctionner. Lorsque vous traitez avec des données externes analogues à des objets ou dans nos fichiers de cas, vous voudrez avoir une configuration prédéfinie qui fonctionnera avec vos tests, mais vous ne voudrez pas non plus ajouter de fausses informations à votre système..

Il existe fondamentalement deux options pour résoudre ce problème, vous pouvez soit simuler les données, comme je l’ai mentionné ci-dessus si vous utilisez les commandes propres au langage pour le chargement des données, vous n’avez pas nécessairement besoin de les tester. Dans de tels cas, vous pouvez simplement fournir les données «récupérées» et poursuivre vos tests, un peu comme ce que nous avons fait avec la chaîne de commande dans la bibliothèque de balises. Mais dans ce cas, nous testons la fonctionnalité récursive que nous ajoutons aux capacités de lecture des fichiers de langues, en fonction de la profondeur spécifiée. Dans de tels cas, vous devez écrire un test et nous devons donc créer des fichiers de démonstration pour tester la lecture du fichier. L’alternative est peut-être de remplacer le fs des fonctions pour exécuter mais ne rien faire, puis nous pouvons compter le nombre de fois que notre fausse fonction a été exécutée ou quelque chose du genre (vérifiez les espions), mais pour notre exemple, je vais simplement créer des fichiers..

Mocha fournit des fonctions qui peuvent être exécutées avant et après vos tests. Vous pouvez ainsi effectuer ce type de configuration et de nettoyage externes autour de vos tests..

Pour notre exemple, nous allons créer deux fichiers de test et des dossiers à deux profondeurs différentes afin de pouvoir tester cette fonctionnalité:

var expect = require ("chai"). expect; var search = require ("… /lib/search.js"); var fs = require ("fs"); describe ("Search", function () decrire ("# scan ()", function () before (function () if (! fs.existsSync (". test_files")) fs.mkdirSync (". test_files "); fs.writeFileSync (". test_files / a "," "); fs.writeFileSync (". test_files / b "," "); fs.mkdirSync (". test_files / dir "); fs.writeFileSync (" .test_files / dir / c "," "); fs.mkdirSync (". test_files / dir2 "); fs.writeFileSync (". test_files / dir2 / d "," ");); après (fonction () fs.unlinkSync (". test_files / dir / c"); fs.rmdirSync (". test_files / dir"); fs.unlinkSync (". test_files / dir2 / d"); fs.rmdirSync (". test_files / dir2 "); fs.unlinkSync (". test_files / a "); fs.unlinkSync (". test_files / b "); fs.rmdirSync (". test_files ");););););

Ceux-ci seront appelés en fonction de la décrire bloquez-les, et vous pouvez même exécuter du code avant et après chaque il bloquer en utilisant avant chaque ou après chaque au lieu. Les fonctions elles-mêmes utilisent simplement des commandes de nœud standard pour créer et supprimer les fichiers, respectivement. Ensuite, nous devons écrire le test réel. Cela devrait aller juste à côté de la après fonction, toujours à l'intérieur du décrire bloc:

 it ("devrait récupérer les fichiers d'un répertoire", function (done) search.scan (". test_files", 0, function (err, flist) expect (flist) .to.deep.equal ([".test_files / a "," .test_files / b "," .test_files / dir / c "," .test_files / dir2 / d "]); done ();););

Ceci est notre premier exemple de test d’une fonction asynchrone, mais comme vous pouvez le constater, c’est aussi simple que jamais; tout ce que nous devons faire est d'utiliser le terminé fonction Mocha fournit dans le il déclarations pour le dire quand on aura fini ce test.

Mocha détectera automatiquement si vous avez spécifié le terminé variable dans le rappel et attendra son appel pour vous permettre de tester le code asynchrone très facilement. En outre, il est à noter que ce modèle est disponible dans Mocha, vous pouvez par exemple l’utiliser dans le avant ou après fonctions si vous aviez besoin de configurer quelque chose de manière asynchrone.

Ensuite, j'aimerais écrire un test qui s'assure que le paramètre de profondeur fonctionne s'il est défini:

 it ("devrait s’arrêter à une profondeur spécifiée", function (done) search.scan (". test_files", 1, function (err, flist) expect (flist) .to.deep.equal ([".test_files / a "," .test_files / b ",]); done ();););

Rien de différent ici, juste un autre test simple. En exécutant ceci dans Mocha, vous obtiendrez une erreur indiquant que la recherche n’a aucune méthode, essentiellement parce que nous n’avons rien écrit. Alors allons ajouter un contour avec la fonction:

var fs = require ("fs"); exports = module.exports = ; exports.scan = fonction (dir, profondeur, terminé) 

Si vous exécutez à nouveau Mocha, il attendra en attente du retour de cette fonction asynchrone, mais comme nous n'avons pas appelé le rappel du tout, le test expire simplement. Par défaut, il devrait expirer après environ deux secondes, mais vous pouvez le régler à l’aide de this.timeout (millisecondes) à l'intérieur d'un bloc decrire ou it, pour ajuster respectivement leurs délais d'expiration.

Cette fonction d'analyse est supposée prendre un chemin et une profondeur et renvoyer une liste de tous les fichiers trouvés. C'est en fait un peu délicat quand vous commencez à penser à la façon dont nous récursons essentiellement deux fonctions différentes ensemble dans une seule fonction. Nous devons parcourir les différents dossiers, qui doivent ensuite se scanner et décider d'aller plus loin..

Faire cela de manière synchrone est acceptable car vous pouvez le parcourir un à un en complétant lentement un niveau ou un chemin à la fois. Lorsque vous utilisez une version asynchrone, cela devient un peu plus compliqué, car vous ne pouvez pas simplement faire une pour chaque boucle ou quelque chose, parce qu'il ne fera pas de pause entre les dossiers, ils seront tous essentiellement exécutés en même temps, chacun renvoyant des valeurs différentes et ils se superposent en quelque sorte.

Donc, pour que cela fonctionne, vous devez créer une sorte de pile dans laquelle vous pouvez traiter de manière asynchrone une à la fois (ou toutes en même temps si vous utilisez une file d’attente à la place), puis conserver un certain ordre de cette manière. C'est un algorithme très spécifique donc je garde juste un extrait de Christopher Jeffrey que vous pouvez trouver sur Stack Overflow. Cela ne s'applique pas uniquement au chargement de fichiers, mais je l'ai utilisé dans un certain nombre d'applications, essentiellement lorsque vous devez traiter un tableau d'objets un par un à l'aide de fonctions asynchrones..

Nous devons le modifier un peu, car nous aimerions avoir une option de profondeur. Comment cette option fonctionne-t-elle lorsque vous définissez le nombre de niveaux de dossiers que vous