Twój pierwszy samouczek dotyczący aplikacji AngularJS, część 2: Narzędzia do tworzenia rusztowań, budowania i testowania
Opublikowany: 2022-03-11Wstęp
Dzięki wielu dostępnym narzędziom pomagającym w tworzeniu aplikacji AngularJS wiele osób ma wrażenie, że jest to niezwykle skomplikowany framework, co wcale nie jest prawdą. To jeden z głównych powodów, dla których rozpocząłem tę serię samouczków.
W części pierwszej omówiliśmy podstawy frameworka AngularJS i zaczęliśmy od napisania naszej pierwszej aplikacji. Ten post jest przeznaczony dla początkujących. Jeśli jesteś bardziej doświadczonym programistą AngularJS, możesz być bardziej zainteresowany demistyfikacją dyrektyw lub historią użycia AngularJS w rozwijającym się startupie.
W tym samouczku odłożymy na bok warstwę logiki aplikacji i nauczymy się przeprowadzać prawidłową konfigurację projektu AngularJS, w tym tworzenie rusztowań, zarządzanie zależnościami i przygotowywanie go do testów (zarówno jednostkowych, jak i end-to-end). Zrobimy to za pomocą narzędzi AngularJS: Yeoman, Grunt i Bower. Następnie przyjrzymy się procesowi pisania i uruchamiania testów Jasmine przy użyciu Karmy.
Karma, Jasmine, Grunt, Bower, Yeoman… Czym są te wszystkie narzędzia?
Jeśli pracujesz z JavaScriptem, jest wysoce prawdopodobne, że znasz już przynajmniej niektóre z tych narzędzi, nawet jeśli jesteś nowy w Angularze. Aby jednak zapewnić wspólny punkt odniesienia, będę unikać jakichkolwiek założeń. Przyjrzyjmy się pokrótce każdej z tych technologii i do czego są przydatne:
Karma (wcześniej znana jako Testacular) jest narzędziem do uruchamiania testów JavaScript Google i naturalnym wyborem do testowania AngularJS. Oprócz umożliwienia uruchamiania testów w prawdziwych przeglądarkach (w tym przeglądarkach na telefony/tablety), jest również niezależny od frameworka testowego ; co oznacza, że możesz go używać w połączeniu z dowolnymi platformami testowymi (takimi jak między innymi Jasmine, Mocha lub QUnit).
Jasmine będzie naszym wyborem, przynajmniej w tym poście. Jego składnia jest bardzo podobna do składni RSpec, jeśli kiedykolwiek z nią pracowałeś. (Jeśli nie, nie martw się; przyjrzymy się temu bardziej szczegółowo w dalszej części tego samouczka).
Grunt to program uruchamiający zadania, który pomaga zautomatyzować kilka powtarzalnych zadań, takich jak minifikacja, kompilacja (lub kompilacja), testowanie i konfigurowanie podglądu aplikacji AngularJS.
Bower to menedżer pakietów, który pomaga znaleźć i zainstalować wszystkie zależności aplikacji, takie jak struktury CSS, biblioteki JavaScript i tak dalej. Działa przez git, podobnie jak bundler Rails, i unika konieczności ręcznego pobierania i aktualizowania zależności.
Yeoman to zestaw narzędzi zawierający 3 podstawowe komponenty: Grunt, Bower i narzędzie do tworzenia rusztowań Yo. Yo generuje kod wzorcowy za pomocą generatorów (które są tylko szablonami rusztowań) i automatycznie konfiguruje Grunt i Bower dla twojego projektu. Możesz znaleźć generatory dla prawie każdego frameworka JavaScript (Angular, Backbone, Ember, itp.), ale ponieważ skupiamy się tutaj na Angular, użyjemy projektu generator-angular.
Wiec gdzie zaczynamy?
Cóż, pierwszą rzeczą, którą musimy zrobić, to zainstalować narzędzia, których będziemy potrzebować.
Jeśli nie masz jeszcze zainstalowanych git, node.js i npm, zainstaluj je.
Następnie przejdziemy do wiersza poleceń i uruchomimy następujące polecenie, aby zainstalować narzędzia Yeoman:
npm install -g yo grunt-cli bower
Aha, i nie zapominaj, że użyjemy generatora AngularJS, więc musisz go również zainstalować:
npm install -g generator-angular
OK, teraz jesteśmy gotowi do…
Zbuduj/wygeneruj naszą aplikację AngularJS
Ostatnim razem ręcznie pożyczyliśmy nasz kod wzorcowy z projektu angular-seed. Tym razem pozwolimy ci (w połączeniu z generatorem-kątowym) zrobić to za nas.
Wszystko, co musimy zrobić, to utworzyć nasz nowy folder projektu, przejść do niego i uruchomić:
yo angular
Zostaną nam przedstawione niektóre opcje, takie jak włączenie lub nie Bootstrap i Compass. Na razie powiedzmy „ nie ” Compassowi i „ tak ” Bootstrapowi. Następnie, gdy pojawi się pytanie o moduły do uwzględnienia (zasób, pliki cookie, oczyszczanie i trasa), wybierzemy tylko angular-route.js
.
Nasze rusztowanie projektowe powinno być teraz utworzone (może to chwilę potrwać), zintegrowane z Karmą i wstępnie skonfigurowane.
Uwaga: pamiętaj, że ograniczamy tutaj moduły do tych, których użyliśmy w aplikacji, którą zbudowaliśmy w pierwszej części tego samouczka. Kiedy robisz to dla własnego projektu, od Ciebie będzie zależeć, które moduły musisz uwzględnić.
Teraz, ponieważ zamierzamy używać Jasmine, dodajmy adapter karma-jasmine
do naszego projektu:
npm install karma-jasmine --save-dev
W przypadku, gdy chcemy, aby nasze testy były wykonywane na instancji Chrome, dodajmy również karma-chrome-launcher
:
npm install karma-chrome-launcher --save-dev
OK, jeśli zrobiliśmy wszystko dobrze, nasze drzewo plików projektu powinno teraz wyglądać tak:
Nasz statyczny kod aplikacji trafia do katalogu app/
, a katalog test/
będzie zawierał (tak, zgadliście!) nasze testy. Pliki, które widzimy w katalogu głównym, to nasze pliki konfiguracyjne projektu. O każdym z nich można się wiele nauczyć, ale na razie pozostaniemy przy domyślnej konfiguracji. Uruchommy więc naszą aplikację po raz pierwszy, co możemy zrobić po prostu za pomocą następującego polecenia:
grunt serve
I voila! Nasza aplikacja powinna teraz pojawić się przed nami!
Trochę o Bower dla AngularJS
Zanim przejdziemy do naprawdę ważnej części (tj. testowania), poświęćmy chwilę, aby dowiedzieć się nieco więcej o Bower. Jak wspomniano wcześniej, Bower jest naszym menedżerem pakietów. Dodanie biblioteki lub wtyczki do naszego projektu można po prostu wykonać za pomocą polecenia bower install
. Na przykład, aby dołączyć modernizr
, wszystko, co musimy zrobić, to (oczywiście w naszym katalogu projektu):
bower install modernizr
Należy jednak pamiętać, że chociaż dzięki temu modernizr
jest częścią naszego projektu (będzie on znajdował się w katalogu app/bower_components
), nadal jesteśmy odpowiedzialni za uwzględnienie go w naszej aplikacji (lub zarządzanie, kiedy powinien być włączony), tak jak byśmy tego potrzebowali zrobić z ręcznie dodaną biblioteką. Jednym ze sposobów, aby to zrobić, byłoby po prostu dodanie następującego tagu <script>
do naszego index.html
:
<script src="bower_components/modernizr/modernizr.js"></script>
Alternatywnie możemy użyć pliku bower.json
do zarządzania naszymi zależnościami. Po dokładnym prześledzeniu wszystkich dotychczasowych kroków, plik bower.json
powinien wyglądać tak:
{ "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" } }
Składnia jest dość oczywista, ale więcej informacji jest dostępnych tutaj.
Możemy wtedy dodać dowolne dodatkowe nowe zależności, które chcemy, a następnie wszystko, czego potrzebujemy, to następująca komenda, aby je zainstalować:
bower install
Teraz napiszmy kilka testów!
OK, teraz nadszedł czas, aby zacząć od miejsca, w którym zakończyliśmy część pierwszą i napisać kilka testów dla naszej aplikacji AngularJS.
Ale najpierw musimy rozwiązać mały problem: chociaż twórcy generatora-angular oparli swój szablon projektu na projekcie angular-seed (który jest oficjalnym szablonem Angular), z jakiegoś powodu, którego tak naprawdę nie rozumiem, zdecydowali aby zmienić konwencje nazewnictwa folderów app
(zmiana css
na styles
, js
na scripts
itd.).

W rezultacie aplikacja, którą pierwotnie napisaliśmy, ma teraz ścieżki niezgodne z wygenerowanym przez nas rusztowaniem. Aby obejść ten problem, pobierz kod aplikacji stąd i pracujmy z tą wersją od tego momentu (w większości jest to dokładnie ta sama aplikacja, którą napisaliśmy, ale ze ścieżkami zaktualizowanymi, aby pasowały do nazewnictwa kątowego generatora).
Po pobraniu aplikacji przejdź do folderu tests/spec/controllers
i utwórz plik o nazwie drivers.js
zawierający następujące elementy:
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"); }); });
To jest pakiet testowy dla naszego driverscontroller
. Może to wyglądać na dużo kodu, ale większość z nich to w rzeczywistości tylko próbna deklaracja danych. Rzućmy okiem na naprawdę ważne elementy:
- Metoda description
describe()
definiuje nasz zestaw testów. - Każda
it()
jest odpowiednią specyfikacją testu. - Każda
beforeEach()
jest wykonywana tuż przed każdym z testów.
Najważniejszym (i potencjalnie mylącym) elementem jest tutaj usługa $httpBackend
, którą utworzyliśmy na zmiennej httpMock
. Ta usługa działa jak fałszywy back-end i odpowiada na nasze wywołania API podczas testów, tak jak robiłby to nasz rzeczywisty serwer w środowisku produkcyjnym. W tym przypadku, używając funkcji expectJSONP()
, ustawiamy ją tak, aby przechwytywała wszelkie żądania JSONP do danego adresu URL (tego samego, którego używamy do uzyskania informacji z serwera) i zamiast tego zwracała statyczną listę z trzema sterownikami, naśladując rzeczywista odpowiedź serwera. Dzięki temu wiemy na pewno, co ma wrócić od kontrolera. Możemy zatem porównać wyniki z tymi oczekiwanymi, używając funkcji expect()
. Jeśli pasują, test przejdzie.
Uruchamianie testów odbywa się po prostu za pomocą polecenia:
grunt test
Zestaw testów dla kontrolera szczegółów sterownika ( drivercontroller
) powinien być dość podobny do tego, który właśnie widzieliśmy. Polecam spróbować samemu to rozgryźć jako ćwiczenie (lub po prostu zajrzyj tutaj, jeśli nie jesteś w stanie tego zrobić).
A co z kompleksowymi testami AngularJS?
Zespół Angulara niedawno wprowadził nowego biegacza do testów end-to-end o nazwie Protractor. Używa webdrivera do interakcji z aplikacją uruchomioną w przeglądarce, a także domyślnie używa frameworka testowego Jasmine, więc składnia będzie wysoce spójna ze składnią naszych testów jednostkowych.
Ponieważ Protractor jest dość nowym narzędziem, jego integracja ze stosem Yeoman i generator-angular
nadal wymaga sporo pracy konfiguracyjnej. Mając to na uwadze i moim zamiarem, aby ten samouczek był jak najprostszy, planuję poświęcić przyszły post wyłącznie na szczegółowe omówienie testów end-to-end w AngularJS.
Wniosek
W tym momencie serii samouczków nauczyliśmy się, jak tworzyć szkielet naszej aplikacji Angular z yo
, zarządzać jej zależnościami za pomocą bower
, a także pisać/uruchamiać testy za pomocą karma
i protractor
. Pamiętaj jednak, że ten samouczek ma na celu jedynie wprowadzenie do tych narzędzi i praktyk AngularJS; nie przeanalizowaliśmy tutaj żadnej z nich dogłębnie.
Naszym celem było po prostu pomóc Ci rozpocząć tę drogę. Od tego momentu możesz kontynuować i dowiedzieć się wszystkiego o tej niesamowitej strukturze i zestawie narzędzi.
Dodatek: Niektóre (ważne) notatki autora
Po przeczytaniu tego samouczka niektórzy mogą zapytać: „Czekaj. Czy nie powinieneś robić tego wszystkiego, zanim zaczniesz kodować swoją aplikację? Czy nie powinna to być pierwsza część tego samouczka?”
Moja krótka odpowiedź brzmi: nie . Jak widzieliśmy w części pierwszej, tak naprawdę nie musisz znać tych wszystkich rzeczy, aby zakodować swoją pierwszą aplikację Angular. Większość narzędzi, które omówiliśmy w tym poście, ma na celu pomóc zoptymalizować przepływ pracy programistycznej i przećwiczyć programowanie oparte na testach (TDD).
Mówiąc o TDD, najbardziej podstawowa koncepcja TDD jest z pewnością rozsądna; mianowicie napisz swoje testy przed napisaniem kodu. Jednak niektórzy ludzie posuwają się za daleko za daleko. TDD to praktyka programistyczna, a nie metoda uczenia się. W związku z tym pisanie testów przed napisaniem kodu ma wiele sensu, podczas gdy nauka pisania testów przed nauczeniem się kodowania nie ma.
Osobiście uważam, że jest to główny powód, dla którego oficjalne samouczki Angulara mogą wydawać się tak zawiłe i mogą być prawie niemożliwe do naśladowania dla osób bez wcześniejszego doświadczenia z interfejsem MVC/TDD. To jeden z głównych powodów, dla których rozpocząłem tę serię samouczków.
Moja osobista rada dla osób uczących się poruszania się po świecie AngularJS brzmi: nie bądź dla siebie zbyt surowy. Nie musisz uczyć się wszystkiego na raz (pomimo tego, że ludzie mówią inaczej!). W zależności od twojego wcześniejszego doświadczenia z innymi frameworkami front-end/testing, AngularJS może być początkowo dość trudny do zrozumienia. Więc naucz się wszystkiego, czego potrzebujesz, aż będziesz w stanie pisać własne proste aplikacje, a następnie, gdy już opanujesz podstawy frameworka, możesz zająć się wybieraniem i stosowaniem długoterminowych praktyk programistycznych, które najlepiej sprawdzają się w przypadku Ty.
Oczywiście to moja skromna opinia i nie wszyscy się z tym zgodzą (a zespół deweloperów Angulara może wysłać po mnie wynajętego zabójcę, gdy to opublikuję), ale to moja wizja i jestem prawie pewien, że jest wielu ludzi tam, kto się ze mną zgodzi.