Lorsque j'ai commencé à jouer avec Ember.js il y a presque un an, l'histoire de la testabilité laissait à désirer. Vous pouvez effectuer des tests unitaires sur un objet sans problème, mais un test unitaire n’est qu’un moyen d’obtenir un retour d’information lorsque vous construisez un logiciel. En plus des tests unitaires, je voulais un moyen de vérifier l'intégration de plusieurs composants. Donc, comme la plupart des gens qui testent des applications JavaScript riches, j'ai choisi le principal outil de test, Selenium..
Avant de commencer, sans une introduction appropriée, il convient de mentionner que Selenium est un excellent moyen de vérifier que votre application Web fonctionne avec une base de données de type production complète et avec toutes vos dépendances de production, etc. Et, du point de vue de l'assurance qualité, cet outil peut être une excellente ressource pour les équipes qui ont besoin de tests d'acceptation d'interface utilisateur de bout en bout.
Mais au fil du temps, une suite de tests apparemment réduite construite sur Selenium peut commencer à ralentir la vitesse de votre équipe. Un moyen simple de réduire cette douleur consiste à éviter de créer une application volumineuse en premier lieu. Si vous créez plutôt quelques applications Web plus petites, cela vous aidera peut-être à vous maintenir à flot un peu plus longtemps, car aucune construction individuelle n'écrase l'équipe, au fur et à mesure de votre croissance..
Mais même sur un petit projet, le vrai problème avec Selenium est que cela ne fait pas partie du processus de développement piloté par les tests. Quand je fais du rouge / vert / refactor, je n’ai pas le temps de réagir lentement sous quelque forme que ce soit. J'avais besoin d'un moyen d'écrire à la fois des tests unitaires et des tests d'intégration qui permettraient un retour rapide d'informations pour m'aider à façonner le logiciel que j'écrivais de manière plus itérative. Si vous utilisez une version de Ember.js> = RC3, vous avez de la chance car écrire un test unitaire ou un test d'intégration est une balade dans la partie..
Maintenant que nous pouvons écrire des tests JavaScript pour notre application, comment les exécutons-nous? La plupart des développeurs ont commencé par utiliser le navigateur directement, mais comme je voulais quelque chose que je pouvais exécuter sans tête depuis la ligne de commande dans un environnement CI avec un écosystème riche en plugins, je me suis tourné vers Karma..
Ce que j’ai aimé de Karma, c’est qu’il ne veut être que votre coureur de test. Peu importe le framework de test JavaScript que vous utilisez ou le framework MVC côté client que vous utilisez. Il est simple de se familiariser avec l'écriture de tests exécutés sur votre application de production Ember.js en quelques lignes de configuration..
Mais avant de pouvoir configurer Karma, nous devons l’installer à l’aide de npm. Je recommande de l'installer localement pour que vos modules npm soient isolés par projet. Pour ce faire, ajoutez un fichier nommé package.json
'à la racine de votre projet qui ressemble à quelque chose comme ci-dessous.
"dépendances": "karma-qunit": "*", "karma": "0.10.2"
Cet exemple nécessitera à la fois Karma et un plugin pour QUnit. Après avoir sauvegardé le package.json
fichier ci-dessus, retournez à la ligne de commande et tapez npm installer
extraire les modules de nœuds requis.
Une fois l’installation de npm terminée, vous verrez maintenant un nouveau dossier portant le nom node_modules
à la racine de votre projet. Ce dossier contient tout le code JavaScript que nous venons de récupérer avec npm, y compris Karma et le plugin QUnit. Si vous approfondissez encore modules_noeud / karma / bin /
vous verrez l'exécutable Karma. Nous allons l'utiliser pour configurer le lanceur de tests, exécuter des tests depuis la ligne de commande, etc..
Ensuite, nous devons configurer le karma pour qu’il sache exécuter les tests QUnit. Type karma init
de la racine du projet. Une liste de questions vous sera demandée. Le premier vous demandera quel framework de test vous souhaitez utiliser, appuyez sur Languette jusqu'à ce que tu vois qunit
, puis frappé Entrer. Réponse suivante non
à la question Require.js, car nous ne l'utilisons pas pour cet exemple d'application. Languette jusqu'à ce que tu vois PhantomJS pour la troisième question et vous aurez besoin de frapper Entrer deux fois car cela permet de multiples options ici. Pour le reste, laissez-les à leur option par défaut.
Lorsque vous avez terminé, vous devriez voir que Karma a généré un fichier de configuration nommé karma.conf.js
dans la racine ou votre projet. Si vous souhaitez en savoir plus sur les différentes options prises en charge par Karma, les commentaires pourraient vous être utiles. Par souci d’exemple, j’ai une version simplifiée du fichier de configuration pour que les choses restent conviviales pour les débutants..
Si vous souhaitez suivre, supprimez le fichier de configuration généré et remplacez-le par celui-ci..
module.exports = fonction (karma) karma.set (basePath: 'js', fichiers: ["vendeur / jquery / jquery.min.js", "vendeur / handlebars / handlebars.js", "vendeur / ember / ember.js "," fournisseur / jquery-mockjax / jquery.mockjax.js "," app.js "," tests / *. js "], logLevel: karma.LOG_ERROR, navigateurs: ['PhantomJS'], singleRun: true, autoWatch: false, frameworks: ["qunit"]); ;
Cela devrait être assez similaire à ce que Karma avait généré plus tôt, je viens de supprimer tous les commentaires et de découper quelques options qui ne nous intéressent pas pour le moment. Pour écrire le premier test unitaire, je devais en dire un peu plus à Karma sur la structure du projet.
En haut du fichier de configuration, vous verrez que j’ai défini le paramètre basePath
à js
parce que tous les actifs JavaScript vivent sous ce dossier dans le projet. Ensuite, j'ai dit à Karma où il peut trouver les fichiers JavaScript nécessaires pour tester notre application simple. Cela inclut jQuery, Handlebars, Ember.js et le app.js
se déposer.
Nous pouvons maintenant ajouter le premier fichier de test unitaire au projet. Commencez par créer un nouveau dossier nommé tests
et nid sous le js
dossier. Ajouter un fichier dans ce nouveau répertoire nommé unit_tests.js
ça ressemble à quelque chose comme ça.
test ('bonjour le monde', fonction () égal (1, 1, ""););
Ce test ne fait encore rien de précieux, mais il nous aidera à vérifier que tout est connecté à Karma pour l'exécuter correctement. Avis dans le karma des dossiers
section, nous avons déjà ajouté le js / tests
annuaire. De cette façon, Karma récupérera tous les fichiers JavaScript que nous utilisons pour tester notre application, à partir de maintenant..
Maintenant que Karma est configuré correctement, exécutez les tests qunit à partir de la ligne de commande en utilisant ./ node_modules / karma / bin / karma start
.
Si tout est configuré correctement, vous devriez voir Karma exécuter un test et le réussir. Pour vérifier qu'il a exécuté le test que nous venons d'écrire, allez le faire échouer en modifiant l'instruction equals. Par exemple, vous pouvez effectuer les opérations suivantes:
test ('bonjour le monde', fonction () égal (1, 2, "boum"););
Si vous pouvez y arriver et le faire passer à nouveau, il est temps d'écrire un test avec un peu plus de but.
Mais avant de commencer, parlons de l'exemple d'application utilisé tout au long de ce post. Dans la capture d'écran ci-dessous, vous voyez que nous avons une grille très simple d'utilisateurs. Dans le tableau HTML, chaque utilisateur est indiqué par son prénom, accompagné d'un bouton permettant de le supprimer. En haut de l'application, vous verrez une entrée pour le prénom, le nom et enfin un bouton qui ajoutera un autre utilisateur à la table lorsque vous cliquez dessus..
https://dl.dropboxusercontent.com/u/716525/content/images/2013/pre-tuts.png
L'exemple d'application présente trois problèmes. Tout d'abord, nous voulons montrer le prénom et le nom de l'utilisateur, pas seulement le prénom. Ensuite, lorsque vous cliquez sur un bouton de suppression, l'utilisateur ne sera pas supprimé. Et enfin, lorsque vous ajoutez un prénom, un nom de famille et que vous cliquez sur Ajouter, cela ne mettra pas un autre utilisateur dans la table..
À première vue, le changement de nom complet semble être le plus simple. Il s’est également avéré être un excellent exemple qui indique quand vous devez écrire un test unitaire, un test d’intégration ou les deux. Dans cet exemple, le moyen le plus rapide d'obtenir des commentaires est d'écrire un test unitaire simple qui affirme que le modèle a une propriété calculée. nom complet
.
Il est facile de tester un objet ember à l'unité. Il vous suffit de créer une nouvelle instance de l'objet et de demander le nom complet
valeur.
test ('la propriété fullName renvoie le premier et le dernier', function () var person = App.Person.create (firstName: 'toran', lastName: 'billups'); var result = person.get ('fullName' ); égal (résultat, 'toran billups', "nom complet était" + résultat););
Suivant si vous revenez à la ligne de commande et exécutez ./ node_modules / karma / bin / karma start
, il devrait montrer un test d'échec avec un message utile décrivant nom complet
comme indéfini actuellement. Pour résoudre ce problème, nous devons ouvrir le app.js
fichier et ajoute une propriété calculée au modèle qui renvoie une chaîne de valeurs combinées du prénom et du nom.
App.Person = Ember.Object.extend (firstName: ", lastName:", nom complet: function () var firstName = this.get ('firstName'); var lastName = this.get ('lastName'); return firstName + "+ lastName; .property ());
Si vous revenez à la ligne de commande et exécutez ./ node_modules / karma / bin / karma start
vous devriez maintenant voir passer un test unitaire. Vous pouvez étendre cet exemple en écrivant quelques autres tests unitaires pour montrer que la propriété calculée doit changer lorsque le prénom ou le nom est mis à jour sur le modèle..
test ('la propriété fullName renvoie le premier et le dernier', function () var person = App.Person.create (firstName: 'toran', lastName: 'billups'); var result = person.get ('fullName' ); égal (résultat, 'toran billups', "nom complet était" + résultat);); test ('la propriété fullName est mise à jour lorsque firstName est modifié', function () var person = App.Person.create (firstName: 'toran', lastName: 'billups'); var result = person.get ('fullName' ); égal (résultat, 'toran facturation', 'nom complet était "+ résultat); personne.set (' prénom ',' wat '); résultat = personne.get (' nom complet '); égal (résultat,' wat facturation ', "nom complet était" + résultat);); test ('la propriété fullName est mise à jour lorsque lastName est modifié', function () var person = App.Person.create (firstName: 'toran', lastName: 'billups'); var result = person.get ('fullName' ); égal (résultat, 'toran billups', "nom complet était" + résultat); personne.set ('nomDernier', 'tbozz'); résultat = personne.get ('nom complet'); égal (résultat, 'toran tbozz ', "nom complet était" + résultat););
Si vous ajoutez ces deux tests supplémentaires et exécutez les trois à partir de la ligne de commande, vous devriez en avoir deux qui échouent. Pour que les trois tests réussissent, modifiez la propriété calculée afin d'écouter les modifications apportées au prénom et au nom de famille. Maintenant si tu cours ./ node_modules / karma / bin / karma start
à partir de la ligne de commande, vous devriez avoir trois tests de réussite.
App.Person = Ember.Object.extend (firstName: ", lastName:", nom complet: function () var firstName = this.get ('firstName'); var lastName = this.get ('lastName'); return firstName + "+ lastName; .property ('firstName', 'lastName'));
Maintenant que nous avons une propriété calculée sur le modèle, nous devons examiner le modèle lui-même car, à l'heure actuelle, nous n'utilisons pas le nouveau nom complet
propriété. Auparavant, vous deviez tout connecter vous-même ou utiliser Selenium pour vérifier que le modèle était correctement rendu. Mais avec ember-testing, vous pouvez maintenant tester cela en ajoutant quelques lignes de JavaScript et un plugin pour Karma..
D'abord ouvrir le package.json
fichier et ajoutez la dépendance karma-ember-preprocessor. Après avoir mis à jour le package.json
déposer, faire npm installer
à partir de la ligne de commande pour tirer ce bas.
"dépendances": "karma-ember-preprocessor": "*", "karma-qunit": "*", "karma": "0.10.2"
Maintenant que le pré-processeur est installé, nous devons informer Karma des fichiers de modèle. dans le des dossiers
section de votre karma.conf.js
file ajoute ce qui suit pour informer Karma des modèles de guidons.
module.exports = fonction (karma) karma.set (basePath: 'js', fichiers: ["vendeur / jquery / jquery.min.js", "vendeur / handlebars / handlebars.js", "vendeur / ember / ember.js "," fournisseur / jquery-mockjax / jquery.mockjax.js "," app.js "," tests / *. js "," modèles / *. handlebars "], logLevel: karma.LOG_ERROR, navigateurs: ['PhantomJS'], singleRun: true, autoWatch: false, frameworks: ["qunit"]); ;
Ensuite, nous devons dire à Karma quoi faire avec ces fichiers de guidon, car techniquement, nous voulons que chaque modèle soit précompilé avant d’être transmis à PhantomJS. Ajoutez la configuration du préprocesseur et pointez n'importe quoi avec une extension de fichier de *.guidon
au préprocesseur de braise. Vous devez également ajouter la configuration des plugins pour enregistrer le pré-processeur ember (ainsi que quelques autres qui sont normalement inclus dans la configuration par défaut de Karma)..
module.exports = fonction (karma) karma.set (basePath: 'js', fichiers: ["vendeur / jquery / jquery.min.js", "vendeur / handlebars / handlebars.js", "vendeur / ember / ember.js "," fournisseur / jquery-mockjax / jquery.mockjax.js "," app.js "," tests / *. js "," modèles / *. handlebars "], logLevel: karma.LOG_ERROR, navigateurs: ['PhantomJS'], singleRun: true, autoWatch: false, frameworks: ["qunit"], plugins: ['karma-qunit', 'karma-chrome-launcher', 'karma-ember-preprocessor', 'karma phantomjs-launcher '], préprocesseurs: "** / *. handlebars":' ember '); ;
Maintenant que nous avons la configuration de Karma pour les tests d’intégration, ajoutez un nouveau fichier nommé integration_tests.js
sous le tests
dossier. Dans ce dossier, nous devons ajouter un simple test pour prouver que nous pouvons gérer l’application Ember.js sans erreur. Ajouter un simple test de qunit pour voir si nous pouvons atteindre le '/'
route et récupère le code HTML de base. Pour le test initial, nous affirmons seulement que le table
La balise existe dans le code HTML généré.
test ('bonjour le monde', fonction () App.reset (); visite ("/"). then (fonction () ok (existe ("table"));));
Notez que nous utilisons quelques aides intégrées aux tests de braises comme visite
et trouver
. le visite
helper est une manière conviviale pour dire à l’application de quel état se trouve lors de l’exécution. Ce test commence au '/'
route car c’est là que les modèles de personnes sont liés au modèle et que notre table HTML est générée. le trouver
helper est un moyen rapide de rechercher des éléments dans le DOM à l'aide de sélecteurs CSS, comme vous le feriez avec jQuery, pour vérifier quelque chose à propos du balisage..
Avant de pouvoir exécuter ce test, nous devons ajouter un fichier d'aide au test qui injectera les aides de test et définira un élément racine générique. Ajoutez le code ci-dessous à un fichier nommé integration_test_helper.js
dans le même tests
annuaire. Cela garantira que notre application dispose des aides de test au moment de l'exécution.
document.write (''); App.rootElement = '# ember-testing'; App.setupForTesting (); App.injectTestHelpers (); function existe (sélecteur) return !! find (sélecteur) .length;
Maintenant, à partir de la ligne de commande, vous devriez pouvoir exécuter le test d'intégration ci-dessus. Si vous avez réussi le test, supprimez la table du modèle de guidon pour la faire échouer (juste pour aider à prouver qu'Ember générait le code HTML à l'aide de ce modèle)..
Maintenant que nous avons configuré les tests d'intégration, il est temps d'écrire celui qui affirme que nous montrons à chaque utilisateur nom complet
au lieu de leur Prénom
. Nous voulons d'abord affirmer que nous obtenons deux lignes, une pour chaque personne.
test ('bonjour le monde', fonction () App.reset (); visite ("/"). then (fonction () var rangées = recherche ("table tr"). longueur; égales (rangées, 2, rangées );););
Remarque: l'application renvoie actuellement des données codées en dur pour que tout reste simple pour le moment. Si vous êtes curieux de savoir pourquoi nous avons deux personnes, voici le trouver
méthode sur le modèle:
App.Person.reopenClass (people: [], find: function () var first = App.Person.create (firstName: 'x', lastName: 'y'); var last = App.Person.create (firstName: 'x', lastName: 'y'); this.people.pushObject (premier); this.people.pushObject (last); retournez this.people;);
Si nous effectuons les tests maintenant, nous devrions tout laisser passer parce que deux personnes sont renvoyées comme prévu. Ensuite, nous devons obtenir la cellule de table qui affiche le nom de la personne et affirme qu’elle utilise la nom complet
propriété au lieu de juste Prénom
.
test ('bonjour le monde', fonction () App.reset (); visite ("/"). then (fonction () var rangées = recherche ("table tr"). longueur; égales (rangées, 2, rangées ); var fullName = find ("table tr: eq (0) td: eq (0)"). text (); égal (fullName, "xy", "la première ligne de la table avait fullName:" + fullName); ););
Si vous exécutez le test ci-dessus, vous devriez voir un test qui échoue car nous n’avons pas encore mis à jour le modèle à utiliser. nom complet
. Maintenant que le test échoue, mettez à jour le modèle à utiliser. nom complet
et lancez les tests avec ./ node_modules / karma / bin / karma start
. Vous devriez maintenant avoir une suite de tests unitaires et d'intégration.
Si vous vous demandez "quand devrais-je écrire un test unitaire par rapport à un test d'intégration?", La réponse est simplement: qu'est-ce qui sera moins douloureux? Si l'écriture d'un test unitaire est plus rapide et que le problème est mieux expliqué qu'un test d'intégration beaucoup plus volumineux, écrivez le test unitaire. Si les tests unitaires semblent moins utiles parce que vous faites du CRUD de base et que le comportement réel réside dans l'interaction entre les composants, écrivez le test d'intégration. Étant donné que les tests d'intégration écrits avec ember-test sont extrêmement rapides, ils font partie du cycle de rétroaction des développeurs et doivent être utilisés de la même manière qu'un test unitaire lorsque cela a du sens..
Pour montrer un test d’intégration du type CRUD en action, écrivez le test suivant pour prouver la ajouter bouton place la personne dans la collection et qu’une nouvelle ligne est rendue dans le modèle de guidon.
test ('add ajoutera une autre personne à la table html', function () App.Person.people = []; App.reset (); visit ("/"). then (function () var rows = find ("table tr"). longueur égale (lignes, 2, "la table avait" + lignes + "lignes"); fillIn (".nom", "foo"); fillIn (".nom", "bar") ; return click (". submit");). then (function () equal (find ("table tr")). length, 3, "la table des personnes n’était pas complète"); equal (find ("table") tr: eq (2) td: eq (0) "). text ()," foo bar "," le nom complet de la personne était incorrect ");););
Commencez par indiquer au test dans quel état vous souhaitez travailler, puis utilisez remplir
assistant, ajoutez un prénom et un nom de famille. Maintenant, si vous cliquez sur le soumettre bouton il devrait ajouter cette personne à la table HTML, donc dans le retour puis
nous pouvons affirmer qu'il existe trois personnes dans le tableau HTML. Exécutez ce test et il devrait échouer car le contrôleur Ember n'est pas terminé..
Pour réussir le test, ajoutez la ligne suivante à la PeopleController
App.PeopleController = Ember.ArrayController.extend (actions: addPerson: function () var person = prenom: this.get ('prenom'), lastName: this.get ('prenom' '); App.Person .add (personne););
Maintenant, si vous lancez les tests avec ./ node_modules / karma / bin / karma start
il devrait montrer trois personnes dans le code HTML rendu.
Le dernier test est la suppression, notez que nous trouvons le bouton pour une ligne spécifique et que nous cliquons dessus. Dans ce qui suit puis
nous vérifions simplement qu'une personne de moins est indiquée dans le tableau HTML.
test ('delete enlèvera la personne pour une ligne donnée', function () App.Person.people = []; App.reset (); visit ("/"). then (function () var rows = find ("table tr"). length; égal (lignes, 2, "la table avait" + lignes + "lignes"); clic de retour ("table .delete: premier");). then (fonction () égal (find ("table tr"). length, 1, "la table des personnes n'était pas complète););")))
Pour obtenir ce passage, ajoutez simplement la ligne suivante à la PeopleController
:
App.PeopleController = Ember.ArrayController.extend (actions: addPerson: function () var person = prenom: this.get ('prenom'), lastName: this.get ('prenom' '); App.Person .add (personne);, deletePerson: fonction (personne) App.Person.remove (personne););
Exécutez les tests à partir de la ligne de commande et vous devriez à nouveau disposer d’une suite de tests.
Cela termine donc notre exemple d'application. N'hésitez pas à poser des questions dans les commentaires.
Si vous préférez utiliser Grunt au lieu du préprocesseur karma-ember, supprimez simplement la configuration des plug-ins et des préprocesseurs. Enlevez aussi modèles / *. guidon
de la section des fichiers car Karma n'aura pas besoin de précompiler les modèles. Voici un simplifié karma.conf.js
cela fonctionne lorsqu’on utilise grunt pour précompiler les modèles de guidons.
module.exports = function (karma) karma.set (basePath: 'js', fichiers: ["lib / deps.min.js", // construit par votre tâche Grunt "tests / *. js"], logLevel : karma.LOG_ERROR, navigateurs: ['PhantomJS'], singleRun: true, autoWatch: false, frameworks: ["qunit"]); ;
Et c'est tout!