En vous assurant que votre application est testée, vous êtes en mesure de réduire le nombre de bogues rencontrés dans votre code, d’accroître la maintenabilité de votre application et de concevoir un code bien structuré..
Les tests unitaires côté client présentent des défis différents des tests côté serveur. Lorsque vous traitez avec du code côté client, vous aurez du mal à séparer la logique d'application de la logique DOM, ainsi que la structuration du code JavaScript en général. Heureusement, il existe un grand nombre d'excellentes bibliothèques de tests côté client permettant de tester votre code, de créer des métriques sur la couverture de tests et d'analyser sa complexité..
Tout d’abord, le test unitaire en général est un moyen de réduire les bugs en veillant à ce que votre application fonctionne comme prévu. Au-delà, les notions de développement piloté par les tests (TDD) et de développement piloté par le comportement (BDD).
Ces deux stratégies de tests unitaires vous aideront à concevoir votre application en écrivant des tests. avant vous écrivez votre logique d'application. En écrivant les tests avant d’écrire le code, vous vous donnez la possibilité de réfléchir à la conception de votre application..
Cela est dû au fait que lorsque vous écrivez des tests, vous essayez en fait de concevoir l'API permettant d'interagir avec votre code. Vous obtenez ainsi un meilleur aperçu de sa conception. Tester en premier vous montrera rapidement les défauts de votre conception, car vous écrivez un code de test qui utilise essentiellement le code que vous écrivez.!
TDD est un processus de découverte de code
Vous apprendrez que TDD vous aide à découvrir votre code au fur et à mesure que vous l'écrivez. Le TDD se résume très rapidement en "Red, Green, Refactor". Cela signifie que vous écrivez un test, écrivez assez de code pour faire le test échouer premier. ensuite, vous écrivez le code qui fait passer votre test. Après cela, vous réfléchissez à ce que vous venez d'écrire et vous le refacturez. Gentil et facile.
BDD est une version légèrement différente de TDD et est davantage basée sur les exigences et les spécifications de l'entreprise..
Il y a de nombreuses raisons pour lesquelles vous devriez tester votre code côté client. Comme mentionné précédemment, cela vous aidera à réduire le nombre de bugs et à concevoir votre application. Les tests côté client sont également importants car ils vous permettent de tester votre code frontal, de manière isolée, loin de votre présentation. En d’autres termes, l’un de ses avantages est de pouvoir tester votre code JavaScript sans faire tourner un serveur d’applications. Vous exécutez simplement les tests et vous vous assurez que les choses fonctionnent sans cliquer et tester des choses. Dans de nombreux cas, vous n’avez même pas besoin d’avoir accès à Internet, tant que vos tests sont configurés correctement..
Le JavaScript jouant un rôle aussi important dans le développement Web moderne, il est important d'apprendre à tester le code et à réduire les risques d'insertion de bogues dans le code de production. Votre patron n'aime pas quand cela se produit et vous ne devriez pas non plus! En fait, un bon endroit pour commencer à travailler avec les tests côté client est d'écrire des tests autour d'un rapport de bogue. Cela vous permettra de vous exercer à écrire des tests lorsque vous n’avez pas d’endroit pour commencer à partir de zéro..
Une autre raison de tester votre code côté client est qu’une fois qu’une série de tests existe et qu’elle couvre correctement votre code, lorsque vous êtes prêt à ajouter de nouvelles fonctionnalités à votre code, vous pourrez ajouter la nouvelle - lancez vos tests et assurez-vous de ne pas régresser et casser les fonctionnalités existantes.
Débuter avec les tests côté client peut être décourageant si vous ne l’avez jamais fait auparavant. L'un des aspects les plus difficiles du test côté client consiste à déterminer le meilleur moyen d'isoler le DOM de la logique de votre application. Cela signifie souvent que vous aurez besoin d'une sorte d'abstraction sur le DOM. Le moyen le plus simple d’atteindre cet objectif consiste à utiliser un cadre côté client tel que Knockout.js, Backbone.js ou Angular.js, pour ne citer que quelques exemples..
Lorsque vous utilisez une telle bibliothèque, vous réfléchissez moins à la manière dont votre page s'affiche dans le navigateur et davantage aux fonctionnalités de votre application. Ce n'est pas comme s'il était impossible de réaliser des tests unitaires avec du JavaScript pur et simple. Dans ce cas, votre vie sera beaucoup plus facile si vous concevez le code de manière à ce que le DOM puisse facilement être extrait..
Il existe de nombreuses bibliothèques de tests parmi lesquelles choisir, bien que les trois premiers coureurs tendent à être QUnit, Moka et Jasmin.
Jasmine et Mocha appartiennent tous les deux à l’école de tests unitaires BDD, alors que QUnit n’est qu’un cadre de tests unitaires à lui tout seul..
Pour le reste de cet article, nous explorerons l'utilisation de QUnit en tant que barrière à l'entrée dans les tests côté client est très faible. Découvrez cette introduction détaillée à QUnit pour plus d'informations.
Débuter avec QUnit est extrêmement simple. Le code HTML suivant est tout ce dont vous avez besoin:
Exemple QUnit
Pour les exemples suivants, supposons que nous construisons un petit widget dans lequel vous entrez un code postal dans une zone de texte et qui renvoie les valeurs de ville, d'état et de comté correspondantes à l'aide de Geonames. Dans un premier temps, il affiche uniquement un code postal, mais dès que celui-ci comporte cinq caractères, il récupère les données de Geonames. S'il est capable de trouver des données, il affichera quelques champs supplémentaires contenant les informations résultantes sur la ville, l'état et le comté. Nous allons également utiliser Knockout.js. La première étape consiste à écrire un test ayant échoué.
En réfléchissant légèrement à la conception avant la rédaction du premier test, il doit probablement y avoir au moins deux viewModels, ce qui constitue un bon point de départ. Pour commencer, nous définirons un module QUnit et notre premier test:
module ("code postal retriever"); test ("les modèles de vue doivent exister", function () ok (FormViewModel, "un viewModel pour notre formulaire doit exister"); ok (AddressViewModel, "un viewModel pour notre adresse doit exister"););
Si vous exécutez ce test, il échouera. Vous pouvez maintenant écrire le code pour le faire passer:
var AddressViewModel = fonction (options) ; var FormViewModel = function () this.address = new AddressViewModel (); ;
Vous verrez le vert plutôt que le rouge cette fois. Des tests comme celui-ci semblent un peu ridicules au début, mais ils sont utiles car ils vous obligent au moins à réfléchir aux premières étapes de votre conception..
Le prochain test que nous écrirons fonctionnera sur le AddressViewModel
la fonctionnalité de. D'après la spécification de ce widget, nous savons que les autres champs doivent d'abord être masqués jusqu'à ce que les données du code postal soient trouvées..
module ("modèle de vue d'adresse"); test ("doit afficher les données d'état de la ville si un code postal est trouvé", function () adresse var = nouveau AddressViewModel (); ok (! adresse.isLocated ()); adresse.zip (12345); adresse.city (" foo "); address.state (" bar "); address.county (" bam "); ok (address.isLocated ()););
Le code correspondant n’a pas encore été écrit, mais l’idée ici est que le est situé
sera un observable calculé, qui retourne vrai
ce n'est que lorsque le code postal, la ville, l'état et le comté sont tous vrais. Donc, ce test échouera au début, maintenant écrivons le code pour le faire passer.
var AddressViewModel = fonction (options) options = options || ; this.zip = ko.observable (options.zip); this.city = ko.observable (options.city); this.state = ko.observable (options.state); this.county = ko.observable (options.county); this.isLocated = ko.computed (function () return this.city () && this.state () && this.county () && this.zip ();, ceci); this.initialize (); ;
Maintenant, si vous exécutez à nouveau les tests, vous verrez vert!
C’est, à la base, comment utiliser TDD pour écrire des tests frontaux. Idéalement, après chaque échec du test, vous écrivez le code le plus simple qui fera passer le test, puis revenez en arrière et refactorisez le code. Cependant, vous pouvez en apprendre beaucoup plus sur les pratiques de TDD. Je suggère donc de les lire et de les étudier plus en profondeur, mais les exemples précédents suffisent à vous faire réfléchir à la possibilité de passer des tests en premier..
Sinon.js est une bibliothèque JavaScript qui permet d'espionner, de stuber et de simuler des objets JavaScript. Lorsque vous écrivez des tests unitaires, vous voulez vous assurer que vous ne pouvez tester qu’une "unité" de code donnée. Cela signifie souvent que vous devrez vous moquer des dépendances pour isoler le code testé..
Sinon a une API extrêmement simple pour faire cela. L’API Geonames prend en charge la récupération de données via un point de terminaison JSONP, ce qui signifie que nous pourrons utiliser facilement $ .ajax
.
Dans l'idéal, vous ne devrez pas nécessairement compter sur l'API Geonames dans vos tests. Ils pourraient être temporairement en panne, votre internet pourrait mourir, et il est aussi plus lent de faire l'appel ajax. Sinon à la rescousse.
test ("devrait seulement essayer d'obtenir des données s'il y a 5 caractères", function () adresse var = nouvelle AddressViewModel (); sinon.stub (jQuery, "ajax"). return (done: $ .noop); address .zip (1234); ok (! jQuery.ajax.calledOnce); address.zip (12345); ok (jQuery.ajax.calledOnce); jQuery.ajax.restore (););
Ici, dans ce test, nous faisons quelques choses. Tout d'abord, le sinon.stub
la fonction va réellement proxy sur jQuery.ajax
et ajoutez la possibilité de voir combien de fois il a été appelé et de nombreuses autres affirmations. Comme le test se lit, "devrait seulement essayer d'obtenir des données s'il y a 5 caractères", nous allons supposer que lorsque l'adresse est définie sur"1234
", qu'aucun appel ajax n'a encore été effectué, puis réglez-le sur"12345
", et à ce moment un appel ajax devrait être fait.
Nous devons ensuite restaurer jQuery.ajax
à son état d’origine, car nous sommes de bons citoyens des tests unitaires et nous voulons que nos tests restent atomiques. Garder vos tests atomiques est important pour vous assurer qu'un test ne dépend pas d'un autre test et qu'il n'y a pas d'état partagé entre les tests. Ils peuvent ensuite être exécutés dans n'importe quel ordre.
Maintenant que le test est écrit, nous pouvons l'exécuter, le regarder échouer, puis écrire le code qui exécute la requête ajax auprès de Geonames..
AddressViewModel.prototype.initialize = function () this.zip.subscribe (this.zipChanged, this); ; AddressViewModel.prototype.zipChanged = fonction (valeur) if (valeur.toChaîne (). Longueur === 5) this.fetch (valeur); ; AddressViewModel.prototype.fetch = fonction (zip) var baseUrl = "http://www.geonames.org/postalCodeLookupJSON" $ .ajax (url: baseUrl, données: "code postal": zip, "pays": " us ", tapez:" GET ", dataType:" JSONP "). done (this.fetched.bind (this)); ;
Ici, nous nous abonnons aux modifications du code postal. Chaque fois que cela change, le zipchangé
la méthode sera appelée. le zipchangé
La méthode vérifiera si la longueur de la valeur du zip est 5
. Quand il atteint 5
, la aller chercher
la méthode sera appelée. Voici où le talon de Sinon entre en jeu. À ce point, $ .ajax
est en fait un talon de Sinon. Alors appeléOnce
sera alors vrai
dans le test.
Le test final que nous écrirons concerne le retour des données du service Geonames:
test ("devrait définir les informations sur la ville en fonction du résultat de la recherche", function () adresse var = nouvelle adresseViewModel (); adresse.fetched (codes postaux: [adminCode1: "foo", adminName2: "bar", placeName: "bam "]); equal (adresse.city ()," bam "); égal (address.state ()," foo "); égal (adresse.county ()," bar "););
Ce test vérifiera comment les données du serveur sont configurées sur le AddressViewmodel
. Exécutez-le, voyez du rouge. Maintenant, fais-le vert:
AddressViewModel.prototype.fetched = fonction (données) var cityInfo; if (data.postalcodes && data.postalcodes.length === 1) cityInfo = data.postalcodes [0]; this.city (cityInfo.placeName); this.state (cityInfo.adminCode1); this.county (cityInfo.adminName2); ;
La méthode récupérée s'assure simplement qu'il existe un tableau de codes postaux
dans les données du serveur, puis définit les propriétés correspondantes sur le voirModèle
.
Vous voyez comme c'est facile maintenant? Une fois que vous avez le flux de faire cela, vous vous retrouverez à peine vouloir ne pas TDD à nouveau. Vous vous retrouvez avec de belles petites fonctions qui sont testables. Vous vous forcez à réfléchir à la manière dont votre code interagit avec ses dépendances. Et vous avez maintenant une série de tests à exécuter lorsqu'une nouvelle exigence est ajoutée au code. Même si vous manquez quelque chose et qu'il y a un bogue dans le code, vous pouvez désormais ajouter simplement un nouveau test à la suite, pour prouver que vous avez corrigé le bogue! Il finit par devenir un peu addictif.
La couverture de test permet d'évaluer facilement la quantité de code testée à l'aide d'un test unitaire. Il est souvent difficile et ne vaut pas la peine d’atteindre une couverture de 100%, mais faites tout votre possible pour l’obtenir aussi haut que possible..
Une des bibliothèques de couverture les plus récentes et les plus faciles s'appelle Blanket.js. Son utilisation avec QUnit est extrêmement simple. Il suffit de saisir le code directement sur leur page d'accueil ou de l'installer avec Bower. Ajoutez ensuite une couverture en tant que bibliothèque au bas de votre qunit.html
fichier, puis ajouter couverture de données
à tous les fichiers que vous voulez des tests de couverture.
Terminé. Super facile, et maintenant vous aurez une option dans votre coureur QUnit pour afficher la couverture:
Dans cet exemple, vous pouvez voir que la couverture de test n'est pas tout à fait à 100%, mais dans ce cas, comme il n'y a pas beaucoup de code, il sera facile de l'augmenter. Vous pouvez réellement explorer les fonctions exactes non encore couvertes:
Ici dans ce cas, le FormViewModel
n'a jamais été instancié dans les tests et la couverture des tests est donc manquante. Vous pouvez ensuite simplement ajouter un nouveau test qui crée une instance du FormViewModel
, et peut-être écrire une assertion qui vérifie que le adresse
la propriété est présente et est un exemple de
la AddressViewModel
.
Vous aurez alors le plaisir de voir une couverture de test à 100%.
Au fur et à mesure que vos applications deviennent de plus en plus grandes, il est agréable de pouvoir exécuter une analyse statique sur votre code JavaScript. Un excellent outil pour exécuter des analyses sur JavaScript s'appelle Plato..
Tu peux courir Platon
en l'installant via npm
avec:
npm install -g plato
Ensuite, vous pouvez courir Platon
sur un répertoire de code JavaScript:
rapports plato -r -d js / app
Ceci exécutera Plato sur tout le JavaScript situé à "js / app
"et afficher les résultats dans rapports
. Plato exécute toutes sortes de métriques sur votre code, y compris des lignes de code moyennes, un score de maintenabilité calculé, JSHint, des erreurs difficiles et estimées, etc..
Il n'y a pas grand-chose à voir dans cette image précédente, tout simplement parce que, pour le code sur lequel nous travaillons, il n'y a qu'un seul fichier, mais lorsque vous commencez à travailler avec une application volumineuse comportant beaucoup de fichiers et de lignes de code , vous trouverez les informations qu’il vous donne extrêmement utiles.
Il garde même une trace de toutes les fois où vous l'avez exécuté, ainsi vous pouvez voir comment vos statistiques changent au fil du temps.
Bien que tester le côté client semble être une proposition difficile, il existe tellement d'outils formidables à utiliser ces jours-ci pour le rendre extrêmement facile. Cet article évalue à peine la surface de toutes les choses qui existent de nos jours pour faciliter les tests côté client. Cela peut être une tâche fastidieuse, mais vous constaterez que les avantages d'une suite de tests et d'un code testable l'emportent de loin sur ses avantages. Espérons qu'avec les étapes décrites ici, vous pourrez commencer à tester rapidement le code côté client..