Votre premier didacticiel d'application AngularJS, partie 2 : Outils pour l'échafaudage, la construction et les tests

Publié: 2022-03-11

introduction

Avec les nombreux outils disponibles pour aider au développement d'applications AngularJS, beaucoup de gens ont l'impression qu'il s'agit d'un framework extrêmement compliqué, ce qui n'est pas du tout le cas. C'est l'une des principales raisons pour lesquelles j'ai commencé cette série de tutoriels.

Dans la première partie, nous avons couvert les bases du framework AngularJS et avons commencé par écrire notre première application. Ce poste est conçu pour les débutants. Si vous êtes un développeur AngularJS plus expérimenté, vous pourriez être plus intéressé par la démystification des directives ou une histoire d'AngularJS utilisé dans une startup en pleine croissance.

Dans ce didacticiel, nous allons mettre de côté la couche logique de l'application et apprendre à configurer correctement le projet AngularJS, y compris l'échafaudage, la gestion des dépendances et à le préparer pour les tests (à la fois unitaires et de bout en bout). Nous le ferons en utilisant ces outils AngularJS : Yeoman, Grunt et Bower. Ensuite, nous passerons en revue le processus d'écriture et d'exécution des tests Jasmine à l'aide de Karma.

Karma, Jasmine, Grunt, Bower, Yeoman… Quels sont tous ces outils ?

Il existe de nombreux outils de développement pour aider à échafauder AngularJS, tester AngularJS et créer correctement une application.

Si vous travaillez avec JavaScript, il est fort probable que vous connaissiez déjà au moins certains de ces outils, même si vous débutez avec Angular. Mais pour aider à assurer une base de référence commune, j'éviterai de faire des suppositions. Passons brièvement en revue chacune de ces technologies et à quoi elles servent :

  • Karma (anciennement connu sous le nom de Testacular) est le testeur JavaScript de Google et le choix naturel pour tester AngularJS. En plus de vous permettre d'exécuter vos tests sur de vrais navigateurs (y compris les navigateurs de téléphone/tablette), il est également indépendant du framework de test ; ce qui signifie que vous pouvez l'utiliser avec n'importe quel framework de test de votre choix (tel que Jasmine, Mocha ou QUnit, entre autres).

  • Jasmine sera notre cadre de test de choix, du moins pour ce post. Sa syntaxe est assez similaire à celle de RSpec, si vous avez déjà travaillé avec cela. (Si ce n'est pas le cas, ne vous inquiétez pas ; nous le verrons plus en détail plus loin dans ce didacticiel.)

  • Grunt est un exécuteur de tâches qui permet d'automatiser plusieurs tâches répétitives, telles que la minification, la compilation (ou la construction), les tests et la configuration d'un aperçu de votre application AngularJS.

  • Bower est un gestionnaire de packages qui vous aide à trouver et à installer toutes vos dépendances d'application, telles que les frameworks CSS, les bibliothèques JavaScript, etc. Il s'exécute sur git, un peu comme le bundler Rails, et évite d'avoir à télécharger et mettre à jour manuellement les dépendances.

  • Yeoman est un ensemble d'outils contenant 3 composants principaux : Grunt, Bower et l'outil d'échafaudage Yo. Yo génère du code passe-partout à l'aide de générateurs (qui ne sont que des modèles d'échafaudage) et configure automatiquement Grunt et Bower pour votre projet. Vous pouvez trouver des générateurs pour presque tous les frameworks JavaScript (Angular, Backbone, Ember, etc.), mais puisque nous nous concentrons ici sur Angular, nous allons utiliser le projet generator-angular.

Alors, par où commencer ?

Eh bien, la première chose que nous devrons faire est d'installer les outils dont nous aurons besoin.

Si vous n'avez pas déjà installé git, node.js et npm, continuez et installez-les.

Ensuite, nous allons accéder à la ligne de commande et exécuter la commande suivante pour installer les outils de Yeoman :

 npm install -g yo grunt-cli bower

Oh, et n'oubliez pas, nous allons utiliser le générateur AngularJS, vous devrez donc l'installer également :

 npm install -g generator-angular

OK, maintenant nous sommes prêts à…

Scaffold/générer notre application AngularJS

La dernière fois, nous avons emprunté manuellement notre code passe-partout au projet angular-seed. Cette fois, nous vous laisserons (en conjonction avec generator-angular) le faire pour nous.

Tout ce que nous avons à faire est de créer notre nouveau dossier de projet, d'y accéder et d'exécuter :

 yo angular

Certaines options nous seront présentées, telles que l'inclusion ou non de Bootstrap et Compass. Pour l'instant, disons non à Compass et oui à Bootstrap. Ensuite, lorsque nous vous demanderons quels modules inclure (ressource, cookies, désinfection et route), nous sélectionnerons uniquement angular-route.js .

Notre échafaudage de projet devrait maintenant être créé (cela peut prendre une minute), intégré à Karma et tout préconfiguré.

Remarque : Gardez à l'esprit que nous limitons les modules ici à ceux que nous avons utilisés dans l'application que nous avons créée dans la première partie de ce didacticiel. Lorsque vous faites cela pour votre propre projet, ce sera à vous de déterminer quels modules vous devrez inclure.

Maintenant, puisque nous allons utiliser Jasmine, ajoutons l'adaptateur karma-jasmine à notre projet :

 npm install karma-jasmine --save-dev

Dans le cas où nous voulons que nos tests soient exécutés sur une instance Chrome, ajoutons également le karma-chrome-launcher :

 npm install karma-chrome-launcher --save-dev

OK, si nous avons tout fait correctement, notre arborescence de fichiers de projet devrait maintenant ressembler à ceci :

Un exemple d'arborescence de fichiers de projet utilisant ces outils AngularJS ressemblera à ceci.

Notre code d'application statique va dans le répertoire app/ et le répertoire test/ contiendra (yup, vous l'avez deviné !) nos tests. Les fichiers que nous voyons à la racine sont nos fichiers de configuration de projet. Il y a beaucoup à apprendre sur chacun d'eux, mais pour l'instant nous nous en tiendrons à la configuration par défaut. Lançons donc notre application pour la première fois, ce que nous pouvons faire simplement avec la commande suivante :

 grunt serve

Et voila ! Notre application devrait maintenant apparaître devant nous !

Un peu sur Bower pour AngularJS

Avant d'entrer dans la partie vraiment importante (c'est-à-dire les tests), prenons une minute pour en savoir un peu plus sur Bower. Comme mentionné précédemment, Bower est notre gestionnaire de paquets. L'ajout d'une bibliothèque ou d'un plugin à notre projet peut simplement être fait en utilisant la commande bower install . Par exemple, pour inclure modernizr , tout ce que nous devons faire est ce qui suit (dans notre répertoire de projet, bien sûr) :

 bower install modernizr

Notez, cependant, que même si cela fait de modernizr une partie de notre projet (il sera situé dans le répertoire app/bower_components ), nous sommes toujours responsables de l'inclure dans notre application (ou de gérer quand il devrait être inclus) car nous aurions besoin à voir avec toute bibliothèque ajoutée manuellement. Une façon de faire serait d'ajouter simplement la <script> suivante à notre index.html :

 <script src="bower_components/modernizr/modernizr.js"></script>

Alternativement, nous pouvons utiliser le fichier bower.json pour gérer nos dépendances. Après avoir suivi attentivement chaque étape jusqu'à présent, le fichier bower.json devrait ressembler à ceci :

 { "name": "F1FeederApp", "version": "0.0.0", "dependencies": { "angular": "1.2.15", "json3": "~3.2.6", "es5-shim": "~2.1.0", "jquery": "~1.11.0", "bootstrap": "~3.0.3", "angular-route": "1.2.15" }, "devDependencies": { "angular-mocks": "1.2.15", "angular-scenario": "1.2.15" } }

La syntaxe est assez explicite, mais plus d'informations sont disponibles ici.

Nous pouvons ensuite ajouter toutes les nouvelles dépendances supplémentaires que nous voulons, puis tout ce dont nous avons besoin est la commande suivante pour les installer :

 bower install

Maintenant, écrivons quelques tests !

OK, maintenant il est temps de reprendre là où nous nous étions arrêtés dans la première partie et d'écrire quelques tests pour notre application AngularJS.

Mais d'abord, il y a un petit problème que nous devons résoudre : bien que les développeurs de generator-angular aient basé leur modèle de projet sur le projet angular-seed (qui est le passe-partout officiel d'Angular), pour une raison que je ne comprends pas vraiment, ils ont décidé pour modifier les conventions de dénomination des dossiers app (changer css en styles , js en scripts , etc.).

Par conséquent, l'application que nous avons écrite à l'origine a maintenant des chemins qui ne correspondent pas à l'échafaudage que nous venons de générer. Pour contourner ce problème, téléchargeons le code de l'application à partir d'ici et travaillons avec cette version à partir de maintenant (il s'agit principalement de la même application que celle que nous avons écrite à l'origine, mais avec les chemins mis à jour pour correspondre à la dénomination angulaire du générateur).

Après avoir téléchargé l'application, accédez au dossier tests/spec/controllers et créez un fichier nommé drivers.js contenant les éléments suivants :

 describe('Controller: driversController', function () { // First, we load the app's module beforeEach(module('F1FeederApp')); // Then we create some variables we're going to use var driversController, scope; beforeEach(inject(function ($controller, $rootScope, $httpBackend) { // Here, we create a mock scope variable, to replace the actual $scope variable // the controller would take as parameter scope = $rootScope.$new(); // Then we create an $httpBackend instance. I'll talk about it below. httpMock = $httpBackend; // Here, we set the httpBackend standard reponse to the URL the controller is // supposed to retrieve from the API httpMock.expectJSONP( "http://ergast.com/api/f1/2013/driverStandings.json?callback=JSON_CALLBACK").respond( {"MRData": {"StandingsTable": {"StandingsLists" : [{"DriverStandings":[ { "Driver": { "givenName": 'Sebastian', "familyName": 'Vettel' }, "points": "397", "nationality": "German", "Constructors": [ {"name": "Red Bull"} ] }, { "Driver": { "givenName": 'Fernando', "familyName": 'Alonso' }, "points": "242", "nationality": "Spanish", "Constructors": [ {"name": "Ferrari"} ] }, { "Driver": { "givenName": 'Mark', "familyName": 'Webber' }, "points": "199", "nationality": "Australian", "Constructors": [ {"name": "Red Bull"} ] } ]}]}}} ); // Here, we actually initialize our controller, passing our new mock scope as parameter driversController = $controller('driversController', { $scope: scope }); // Then we flush the httpBackend to resolve the fake http call httpMock.flush(); })); // Now, for the actual test, let's check if the driversList is actually retrieving // the mock driver array it('should return a list with three drivers', function () { expect(scope.driversList.length).toBe(3); }); // Let's also make a second test checking if the drivers attributes match against // the expected values it('should retrieve the family names of the drivers', function () { expect(scope.driversList[0].Driver.familyName).toBe("Vettel"); expect(scope.driversList[1].Driver.familyName).toBe("Alonso"); expect(scope.driversList[2].Driver.familyName).toBe("Webber"); }); });

Il s'agit de la suite de tests pour notre contrôleur de driverscontroller . Cela peut ressembler à beaucoup de code, mais la majeure partie n'est en fait qu'une fausse déclaration de données. Passons rapidement en revue les éléments vraiment importants :

  • La méthode describe() définit notre suite de tests.
  • Chaque it() est une spécification de test appropriée.
  • Chaque fonction beforeEach() est exécutée juste avant chacun des tests.

L'élément le plus important (et potentiellement déroutant) ici est le service $httpBackend que nous avons instancié sur la variable httpMock . Ce service agit comme un faux back-end et répond à nos appels d'API lors des tests, tout comme notre serveur réel le ferait en production. Dans ce cas, en utilisant la fonction expectJSONP() , nous la configurons pour intercepter toutes les requêtes JSONP à l'URL donnée (la même que nous utilisons pour obtenir les informations du serveur) et à la place, renvoyons une liste statique avec trois pilotes, imitant le vraie réponse du serveur. Cela nous permet de savoir avec certitude ce qui est censé revenir du contrôleur. On peut donc comparer les résultats avec ceux attendus, grâce à la fonction expect() . S'ils correspondent, le test réussira.

L'exécution des tests se fait simplement avec la commande :

 grunt test

La suite de tests pour le contrôleur de détails du pilote ( drivercontroller ) devrait être assez similaire à celle que nous venons de voir. Je vous recommande d'essayer de le découvrir par vous-même comme exercice (ou vous pouvez simplement jeter un œil ici, si vous n'êtes pas à la hauteur).

Qu'en est-il des tests AngularJS de bout en bout ?

L'équipe Angular a récemment introduit un nouveau coureur pour les tests de bout en bout appelé Protractor. Il utilise webdriver pour interagir avec l'application exécutée dans le navigateur et il utilise également le framework de test Jasmine par défaut, de sorte que la syntaxe sera très cohérente avec celle de nos tests unitaires.

Étant donné que Protractor est un outil relativement nouveau, son intégration avec la pile Yeoman et le generator-angular nécessite toujours une bonne quantité de travail de configuration. Dans cet esprit, et mon intention de garder ce didacticiel aussi simple que possible, mon plan est de consacrer un futur article exclusivement à la couverture approfondie des tests de bout en bout dans AngularJS.

Conclusion

À ce stade de la série de didacticiels, nous avons appris à échafauder notre application Angular avec yo , à gérer ses dépendances avec bower et à écrire/exécuter des tests à l'aide de karma et protractor . Gardez à l'esprit, cependant, que ce tutoriel se veut uniquement une introduction à ces outils et pratiques AngularJS ; nous n'en avons analysé aucun ici en profondeur.

Notre objectif a simplement été de vous aider à vous lancer dans cette voie. À partir de là, c'est à vous de continuer et d'apprendre tout ce que vous pouvez sur cet incroyable cadre et cette suite d'outils.

Addendum : Quelques notes (importantes) de l'auteur

Après avoir lu ce didacticiel, certaines personnes peuvent demander : « Attendez. N'êtes-vous pas censé faire tout cela avant de commencer à coder votre application ? Cela n'aurait-il pas dû faire partie de ce tutoriel ? »

Ma réponse courte à cela est non . Comme nous l'avons vu dans la première partie, vous n'avez pas besoin de connaître tout cela pour coder votre première application Angular. Au contraire, la plupart des outils dont nous avons parlé dans cet article sont conçus pour vous aider à optimiser votre flux de travail de développement et à pratiquer le développement piloté par les tests (TDD).

Et en parlant de TDD, le concept le plus fondamental de TDD est certainement un bon ; à savoir, écrivez vos tests avant d'écrire votre code. Certaines personnes, cependant, poussent ce concept trop loin. TDD est une pratique de développement, pas une méthode d'apprentissage. En conséquence, écrire vos tests avant d'écrire votre code a beaucoup de sens, alors qu'apprendre à écrire vos tests avant d'apprendre à coder ne l'est pas.

Personnellement, je pense que c'est la principale raison pour laquelle les didacticiels officiels d'Angular peuvent sembler si alambiqués et peuvent être presque impossibles à suivre pour les personnes n'ayant aucune expérience préalable de MVC/TDD. C'est l'une des principales raisons pour lesquelles j'ai commencé cette série de tutoriels.

Mon conseil personnel pour ceux qui apprennent à naviguer dans le monde AngularJS est le suivant : ne soyez pas trop dur avec vous-même. Vous n'avez pas besoin de tout apprendre d'un coup (bien que les gens vous disent le contraire !). En fonction de votre expérience antérieure avec d'autres frameworks frontaux/de test, AngularJS peut être assez difficile à comprendre au départ. Apprenez donc tout ce que vous devez apprendre jusqu'à ce que vous soyez capable d'écrire vos propres applications simples, puis, une fois que vous êtes à l'aise avec les bases du framework, vous pouvez vous préoccuper de sélectionner et d'appliquer les pratiques de développement à long terme qui fonctionnent le mieux pour toi.

Bien sûr, c'est mon humble avis et tout le monde ne sera pas d'accord avec cette approche (et l'équipe de développement d'Angular pourrait envoyer un tueur à gages après moi une fois que j'aurai publié ceci), mais c'est ma vision et je suis presque sûr qu'il y a beaucoup de gens là-bas qui sera d'accord avec moi.

En relation: Tutoriel Angular 6: Nouvelles fonctionnalités avec une nouvelle puissance