Ihr erstes AngularJS-App-Tutorial Teil 2: Tools für Scaffolding, Building und Testing
Veröffentlicht: 2022-03-11Einführung
Bei den vielen Tools, die zur Unterstützung bei der Entwicklung von AngularJS-Anwendungen zur Verfügung stehen, haben viele den Eindruck, dass es sich um ein extrem kompliziertes Framework handelt, was überhaupt nicht der Fall ist. Das ist einer der Hauptgründe, warum ich diese Tutorial-Reihe gestartet habe.
In Teil eins haben wir die Grundlagen des AngularJS-Frameworks behandelt und mit dem Schreiben unserer ersten Anwendung begonnen. Dieser Beitrag ist für Anfänger gedacht. Wenn Sie ein erfahrenerer AngularJS-Entwickler sind, interessieren Sie sich möglicherweise eher für die Entmystifizierung von Anweisungen oder für eine Geschichte von AngularJS im Einsatz bei einem wachsenden Startup.
In diesem Tutorial werden wir die Ebene der Anwendungslogik beiseite legen und lernen, wie man ein ordnungsgemäßes AngularJS-Projekt-Setup durchführt, einschließlich Gerüstbau, Abhängigkeitsmanagement und Vorbereitung zum Testen (sowohl Unit als auch End-to-End). Wir werden dies mit diesen AngularJS-Tools tun: Yeoman, Grunt und Bower. Dann sehen wir uns den Prozess des Schreibens und Ausführens von Jasmine-Tests mit Karma an.
Karma, Jasmine, Grunt, Bower, Yeoman … Was sind all diese Tools?
Wenn Sie mit JavaScript arbeiten, ist es sehr wahrscheinlich, dass Sie zumindest einige dieser Tools bereits kennen, selbst wenn Sie neu bei Angular sind. Aber um eine gemeinsame Grundlinie sicherzustellen, vermeide ich es, Annahmen zu treffen. Lassen Sie uns kurz auf jede dieser Technologien und ihre Nützlichkeit eingehen:
Karma (früher bekannt als Testacular) ist Googles JavaScript-Test-Runner und die natürliche Wahl zum Testen von AngularJS. Es ermöglicht Ihnen nicht nur, Ihre Tests auf echten Browsern (einschließlich Telefon-/Tablet-Browsern) auszuführen, sondern ist auch Test-Framework-agnostisch ; Das bedeutet, dass Sie es in Verbindung mit jedem Testframework Ihrer Wahl verwenden können (wie unter anderem Jasmine, Mocha oder QUnit).
Jasmine wird unser bevorzugtes Test-Framework sein, zumindest für diesen Beitrag. Seine Syntax ist der von RSpec ziemlich ähnlich, falls Sie jemals damit gearbeitet haben. (Wenn nicht, machen Sie sich keine Sorgen; wir werden es später in diesem Tutorial genauer untersuchen.)
Grunt ist ein Task-Runner, der hilft, mehrere sich wiederholende Aufgaben zu automatisieren, wie z. B. Minimierung, Kompilierung (oder Build), Testen und Einrichten einer Vorschau Ihrer AngularJS-Anwendung.
Bower ist ein Paketmanager, der Ihnen hilft, alle Ihre Anwendungsabhängigkeiten wie CSS-Frameworks, JavaScript-Bibliotheken usw. zu finden und zu installieren. Es läuft über Git, ähnlich wie Rails Bundler, und vermeidet die Notwendigkeit, Abhängigkeiten manuell herunterzuladen und zu aktualisieren.
Yeoman ist ein Toolset, das 3 Kernkomponenten enthält: Grunt, Bower und das Gerüstwerkzeug Yo. Yo generiert mit Hilfe von Generatoren (die nur Gerüstvorlagen sind) Boilerplate-Code und konfiguriert automatisch Grunt und Bower für Ihr Projekt. Sie finden Generatoren für fast jedes JavaScript-Framework (Angular, Backbone, Ember usw.), aber da wir uns hier auf Angular konzentrieren, verwenden wir das Generator-Angular-Projekt.
Also, wo fangen wir an?
Nun, das erste, was wir tun müssen, ist, die Tools zu installieren, die wir brauchen werden.
Wenn Sie git, node.js und npm noch nicht installiert haben, fahren Sie fort und installieren Sie sie.
Dann gehen wir zur Befehlszeile und führen den folgenden Befehl aus, um die Tools von Yeoman zu installieren:
npm install -g yo grunt-cli bower
Oh, und vergessen Sie nicht, wir werden den AngularJS-Generator verwenden, also müssen Sie ihn auch installieren:
npm install -g generator-angular
OK, jetzt sind wir bereit für …
Erstellen Sie ein Gerüst/generieren Sie unsere AngularJS-Anwendung
Letztes Mal haben wir unseren Boilerplate-Code manuell aus dem Angle-Seed-Projekt ausgeliehen. Diesmal lassen wir Sie (in Verbindung mit Generator-Winkel) das für uns tun.
Alles, was wir tun müssen, ist unseren neuen Projektordner zu erstellen, dorthin zu navigieren und Folgendes auszuführen:
yo angular
Uns werden einige Optionen angezeigt, z. B. ob Bootstrap und Compass einbezogen werden sollen oder nicht. Sagen wir erstmal nein zu Compass und ja zu Bootstrap. Wenn Sie dann gefragt werden, welche Module enthalten sein sollen (Ressource, Cookies, Bereinigung und Route), wählen wir nur angular-route.js
.
Unser Projekt-Scaffold sollte nun erstellt sein (das kann eine Minute dauern), in Karma integriert und alles vorkonfiguriert sein.
Hinweis: Denken Sie daran, dass wir die Module hier auf diejenigen beschränken, die wir in der Anwendung verwendet haben, die wir in Teil 1 dieses Tutorials erstellt haben. Wenn Sie dies für Ihr eigenes Projekt tun, liegt es an Ihnen, zu bestimmen, welche Module Sie einbeziehen müssen.
Da wir jetzt Jasmine verwenden werden, fügen wir unserem Projekt den karma-jasmine
Adapter hinzu:
npm install karma-jasmine --save-dev
Falls wir möchten, dass unsere Tests auf einer Chrome-Instanz ausgeführt werden, fügen wir auch den karma-chrome-launcher
hinzu:
npm install karma-chrome-launcher --save-dev
OK, wenn wir alles richtig gemacht haben, sollte unser Projektdateibaum jetzt so aussehen:
Unser statischer Anwendungscode geht in das app/
-Verzeichnis und das test/
-Verzeichnis enthält (yup, Sie haben es erraten!) unsere Tests. Die Dateien, die wir im Stammverzeichnis sehen, sind unsere Projektkonfigurationsdateien. Über jeden von ihnen gibt es viel zu lernen, aber im Moment bleiben wir einfach bei der Standardkonfiguration. Lassen Sie uns also unsere App zum ersten Mal ausführen, was wir einfach mit dem folgenden Befehl tun können:
grunt serve
Und voila! Unsere App sollte jetzt vor uns erscheinen!
Ein wenig über Bower für AngularJS
Bevor wir zum wirklich wichtigen Teil kommen (dh dem Testen), nehmen wir uns eine Minute Zeit, um etwas mehr über Bower zu erfahren. Wie bereits erwähnt, ist Bower unser Paketmanager. Das Hinzufügen einer Bibliothek oder eines Plugins zu unserem Projekt kann einfach mit dem bower install
erfolgen. Um beispielsweise modernizr
, müssen wir lediglich Folgendes tun (natürlich in unserem Projektverzeichnis):
bower install modernizr
Beachten Sie jedoch, dass modernizr
zwar Teil unseres Projekts wird (es wird sich im Verzeichnis app/bower_components
befinden), wir aber dennoch dafür verantwortlich sind, es nach Bedarf in unsere Anwendung aufzunehmen (oder zu verwalten, wann es aufgenommen werden soll). mit manuell hinzugefügten libs zu tun. Eine Möglichkeit, dies zu tun, wäre, einfach das folgende <script>
-Tag zu unserer index.html
hinzuzufügen:
<script src="bower_components/modernizr/modernizr.js"></script>
Alternativ können wir die Datei bower.json
verwenden, um unsere Abhängigkeiten zu verwalten. Nachdem Sie bis jetzt jeden Schritt sorgfältig befolgt haben, sollte die Datei bower.json
wie folgt aussehen:
{ "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" } }
Die Syntax ist ziemlich selbsterklärend, aber weitere Informationen finden Sie hier.
Wir können dann alle zusätzlichen neuen Abhängigkeiten hinzufügen, die wir wollen, und dann brauchen wir nur noch den folgenden Befehl, um sie zu installieren:
bower install
Lass uns jetzt ein paar Tests schreiben!
OK, jetzt ist es an der Zeit, dort weiterzumachen, wo wir in Teil eins aufgehört haben, und einige Tests für unsere AngularJS-App zu schreiben.
Aber zuerst gibt es ein kleines Problem, das wir ansprechen müssen: Obwohl die Entwickler von Generator-Angular ihre Projektvorlage auf dem Angle-Seed-Projekt (das die offizielle Angular-Boilerplate ist) basierten, entschieden sie sich aus irgendeinem Grund, den ich nicht wirklich verstehe , um die Benennungskonventionen für app
-Ordner zu ändern (Ändern von css
in styles
, js
in scripts
und so weiter).

Infolgedessen hat die App, die wir ursprünglich geschrieben haben, jetzt Pfade, die nicht mit dem soeben generierten Gerüst übereinstimmen. Um dies zu umgehen, laden wir den App-Code von hier herunter und arbeiten von diesem Punkt an mit dieser Version (es ist meistens genau dieselbe App, die wir ursprünglich geschrieben haben, aber mit aktualisierten Pfaden, um mit der Benennung im Generatorwinkel übereinzustimmen).
Navigieren Sie nach dem Herunterladen der App zum Ordner „ tests/spec/controllers
“ und erstellen Sie eine Datei mit dem Namen drivers.js
, die Folgendes enthält:
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"); }); });
Dies ist die Testsuite für unseren driverscontroller
. Es mag wie eine Menge Code aussehen, aber das meiste davon ist eigentlich nur Scheindatendeklaration. Werfen wir einen kurzen Blick auf die wirklich wichtigen Elemente:
- Die Methode
describe()
definiert unsere Testsuite. - Jedes
it()
ist eine richtige Testspezifikation. - Jede
beforeEach()
Funktion wird direkt vor jedem der Tests ausgeführt.
Das wichtigste (und potenziell verwirrende) Element hier ist der $httpBackend
-Dienst, den wir in der httpMock
Variablen instanziiert haben. Dieser Dienst fungiert als gefälschtes Back-End und reagiert auf unsere API-Aufrufe bei den Testläufen, genau wie es unser tatsächlicher Server in der Produktion tun würde. In diesem Fall stellen wir sie mit der Funktion expectJSONP()
so ein, dass sie alle JSONP-Anforderungen an die angegebene URL abfängt (die gleiche, die wir verwenden, um die Informationen vom Server abzurufen) und stattdessen eine statische Liste mit drei Treibern zurückgeben, die die nachahmen echte Serverantwort. Dadurch können wir sicher wissen, was vom Controller zurückkommen soll. Wir können daher die Ergebnisse mit den erwarteten vergleichen, indem wir die Funktion expect()
verwenden. Wenn sie übereinstimmen, wird der Test bestanden.
Das Ausführen der Tests erfolgt einfach mit dem Befehl:
grunt test
Die Testsuite für den Treiberdetails-Controller ( drivercontroller
) sollte der gerade gesehenen recht ähnlich sein. Ich empfehle Ihnen, es als Übung selbst herauszufinden (oder Sie können einfach hier nachsehen, wenn Sie nicht dazu in der Lage sind).
Was ist mit End-to-End-AngularJS-Tests?
Das Angular-Team hat kürzlich einen neuen Runner für End-to-End-Tests namens Protractor vorgestellt. Es verwendet Webdriver, um mit der Anwendung zu interagieren, die im Browser ausgeführt wird, und es verwendet standardmäßig auch das Jasmine-Testframework, sodass die Syntax in hohem Maße mit der unserer Komponententests übereinstimmt.
Da Protractor ein ziemlich neues Tool ist, erfordert seine Integration mit dem Yeoman-Stack und generator-angular
noch eine Menge Konfigurationsarbeit. In Anbetracht dessen und meiner Absicht, dieses Tutorial so einfach wie möglich zu halten, plane ich, einen zukünftigen Beitrag ausschließlich der ausführlichen Behandlung von End-to-End-Tests in AngularJS zu widmen.
Fazit
An diesem Punkt der Tutorial-Serie haben wir gelernt, wie wir unsere Angular-App mit yo
aufbauen, ihre Abhängigkeiten mit bower
verwalten und einige Tests mit karma
und protractor
schreiben/ausführen. Beachten Sie jedoch, dass dieses Tutorial nur als Einführung in diese AngularJS-Tools und -Praktiken gedacht ist. Wir haben hier keinen von ihnen ausführlich analysiert.
Unser Ziel war es einfach, Ihnen dabei zu helfen, diesen Weg einzuschlagen. Von hier aus liegt es an Ihnen, weiterzumachen und alles über dieses erstaunliche Framework und die Suite von Tools zu lernen.
Nachtrag: Einige (wichtige) Anmerkungen des Autors
Nachdem Sie dieses Tutorial gelesen haben, werden einige Leute fragen: „Warte. Sollten Sie all diese Dinge nicht erledigen, bevor Sie mit dem Programmieren Ihrer App beginnen? Hätte das nicht Teil eins dieses Tutorials sein sollen?“
Meine kurze Antwort darauf ist nein . Wie wir in Teil eins gesehen haben, müssen Sie all diese Dinge nicht wirklich wissen, um Ihre erste Angular-App zu programmieren. Vielmehr sind die meisten Tools, die wir in diesem Beitrag besprochen haben, darauf ausgelegt, Ihnen dabei zu helfen, Ihren Entwicklungsworkflow zu optimieren und Test-Driven Development (TDD) zu praktizieren.
Apropos TDD, das grundlegendste Konzept von TDD ist sicherlich ein solides; Schreiben Sie nämlich Ihre Tests, bevor Sie Ihren Code schreiben. Einige Leute gehen mit diesem Konzept jedoch viel zu weit. TDD ist eine Entwicklungspraxis, keine Lernmethode. Dementsprechend ist es sehr sinnvoll, Ihre Tests zu schreiben , bevor Sie Ihren Code schreiben, während Sie lernen , wie Sie Ihre Tests schreiben, bevor Sie lernen, wie man codiert.
Ich persönlich denke, dass dies der Hauptgrund ist, warum sich die offiziellen Angular-Tutorials so verworren anfühlen und für Leute ohne vorherige Front-End-MVC/TDD-Erfahrung fast unmöglich zu befolgen sind. Das ist einer der Hauptgründe, warum ich diese Tutorial-Reihe gestartet habe.
Mein persönlicher Rat für diejenigen, die lernen, sich in der AngularJS-Welt zurechtzufinden, lautet: Seien Sie nicht zu streng zu sich selbst. Sie müssen nicht alles auf einmal lernen (auch wenn die Leute Ihnen etwas anderes sagen!). Abhängig von Ihren bisherigen Erfahrungen mit anderen Front-End-/Test-Frameworks kann AngularJS anfangs ziemlich schwer zu verstehen sein. Lernen Sie also alles, was Sie lernen müssen, bis Sie in der Lage sind, Ihre eigenen einfachen Apps zu schreiben, und sobald Sie mit den Grundlagen des Frameworks vertraut sind, können Sie sich mit der Auswahl und Anwendung der langfristigen Entwicklungsmethoden befassen, die am besten funktionieren Sie.
Natürlich ist das meine bescheidene Meinung und nicht jeder wird diesem Ansatz zustimmen (und das Angular-Entwicklerteam schickt mir vielleicht einen angeheuerten Mörder nach, sobald ich dies veröffentliche), aber das ist meine Vision, und ich bin mir ziemlich sicher, dass das viele Leute sind da draußen, wer wird mir zustimmen.