Il tuo primo tutorial sull'app AngularJS Parte 2: strumenti per impalcature, costruzione e test
Pubblicato: 2022-03-11introduzione
Con i numerosi strumenti disponibili per aiutare nello sviluppo di applicazioni AngularJS, molte persone hanno l'impressione che si tratti di un framework estremamente complicato, il che non è affatto il caso. Questo è uno dei motivi principali per cui ho iniziato questa serie di tutorial.
Nella prima parte abbiamo trattato le basi del framework AngularJS e abbiamo iniziato scrivendo la nostra prima applicazione. Questo post è progettato per i principianti. Se sei uno sviluppatore AngularJS più esperto, potresti essere più interessato a demistificare le direttive o una storia di AngularJS in uso in una startup in crescita.
In questo tutorial, metteremo da parte il livello della logica dell'applicazione e impareremo come condurre la corretta configurazione del progetto AngularJS, inclusi lo scaffolding, la gestione delle dipendenze e la preparazione per il test (sia unitario che end-to-end). Lo faremo usando questi strumenti AngularJS: Yeoman, Grunt e Bower. Quindi, esamineremo il processo di scrittura ed esecuzione dei test Jasmine utilizzando Karma.
Karma, Jasmine, Grunt, Bower, Yeoman... Quali sono tutti questi strumenti?
Se lavori con JavaScript, è molto probabile che tu conosca già almeno alcuni di questi strumenti, anche se non conosci Angular. Ma per aiutare a garantire una linea di base comune, eviterò di fare supposizioni. Esaminiamo brevemente ciascuna di queste tecnologie e a cosa serve:
Karma (precedentemente noto come Testacular) è il test runner JavaScript di Google e la scelta naturale per testare AngularJS. Oltre a consentire di eseguire i test su browser reali (inclusi browser per telefoni/tablet), è anche indipendente dal framework di test ; il che significa che puoi usarlo insieme a qualsiasi framework di test di tua scelta (come Jasmine, Mocha o QUnit, tra gli altri).
Jasmine sarà il nostro framework di test preferito, almeno per questo post. La sua sintassi è abbastanza simile a quella di RSpec, se ci hai mai lavorato. (Se non lo hai fatto, non ti preoccupare; lo verificheremo più in dettaglio più avanti in questo tutorial.)
Grunt è un task runner che aiuta ad automatizzare diverse attività ripetitive, come minimizzazione, compilazione (o build), test e configurazione di un'anteprima dell'applicazione AngularJS.
Bower è un gestore di pacchetti che ti aiuta a trovare e installare tutte le dipendenze delle tue applicazioni, come framework CSS, librerie JavaScript e così via. Funziona su git, proprio come Rails bundler, ed evita la necessità di scaricare e aggiornare manualmente le dipendenze.
Yeoman è un set di strumenti contenente 3 componenti principali: Grunt, Bower e lo strumento per impalcature Yo. Yo genera codice boilerplate con l'aiuto di generatori (che sono solo modelli di scaffolding) e configura automaticamente Grunt e Bower per il tuo progetto. Puoi trovare generatori per quasi tutti i framework JavaScript (Angular, Backbone, Ember, ecc.), Ma poiché qui ci stiamo concentrando su Angular, utilizzeremo il progetto generator-angular.
Allora, da dove iniziamo?
Bene, la prima cosa che dovremo fare è installare gli strumenti di cui avremo bisogno.
Se non hai già installato git, node.js e npm, vai avanti e installali.
Quindi andremo alla riga di comando ed eseguiremo il seguente comando per installare gli strumenti di Yeoman:
npm install -g yo grunt-cli bower
Oh, e non dimenticare, useremo il generatore AngularJS, quindi dovrai installarlo anche tu:
npm install -g generator-angular
OK, ora siamo pronti per...
Impalcatura/genera la nostra applicazione AngularJS
L'ultima volta, abbiamo preso in prestito manualmente il nostro codice standard dal progetto angular-seed. Questa volta, lasceremo che yo (in combinazione con generator-angular) lo faccia per noi.
Tutto quello che dobbiamo fare è creare la nostra nuova cartella del progetto, accedervi ed eseguire:
yo angular
Ci verranno presentate alcune opzioni, ad esempio se includere o meno Bootstrap e Compass. Per ora diciamo no a Compass e sì a Bootstrap. Quindi, quando richiesto sui moduli da includere (risorsa, cookie, sanitize e route), selezioneremo solo angular-route.js
.
Il nostro scaffold di progetto dovrebbe ora essere creato (potrebbe volerci un minuto), integrato con Karma e tutto preconfigurato.
Nota: tieni presente che stiamo limitando i moduli qui a quelli che abbiamo usato nell'applicazione che abbiamo creato nella prima parte di questo tutorial. Quando lo fai per il tuo progetto, sta a te determinare quali moduli dovrai includere.
Ora, dal momento che useremo Jasmine, aggiungiamo l'adattatore karma-jasmine
al nostro progetto:
npm install karma-jasmine --save-dev
Nel caso in cui desideriamo che i nostri test vengano eseguiti su un'istanza di Chrome, aggiungiamo anche karma-chrome-launcher
:
npm install karma-chrome-launcher --save-dev
OK, se abbiamo fatto tutto bene, il nostro albero dei file di progetto ora dovrebbe apparire così:
Il nostro codice dell'applicazione statica va nella directory app/
e la directory test/
conterrà (sì, hai indovinato!) i nostri test. I file che vediamo nella radice sono i nostri file di configurazione del progetto. C'è molto da imparare su ognuno di essi, ma per ora ci limiteremo alla configurazione predefinita. Quindi eseguiamo la nostra app per la prima volta, cosa che possiamo fare semplicemente con il seguente comando:
grunt serve
E voilà! La nostra app dovrebbe ora apparire di fronte a noi!
Un po' di Bower per AngularJS
Prima di entrare nella parte veramente importante (cioè, il test), prendiamoci un minuto per imparare un po' di più su Bower. Come accennato in precedenza, Bower è il nostro gestore di pacchetti. L'aggiunta di una lib o di un plug-in al nostro progetto può essere eseguita semplicemente utilizzando il comando di bower install
. Ad esempio, per includere modernizr
, tutto ciò che dobbiamo fare è quanto segue (all'interno della nostra directory di progetto, ovviamente):
bower install modernizr
Nota, tuttavia, che sebbene ciò renda modernizr
parte del nostro progetto (si troverà nella directory app/bower_components
), siamo comunque responsabili dell'inclusione nella nostra applicazione (o della gestione quando dovrebbe essere inclusa) poiché avremmo bisogno a che fare con qualsiasi lib aggiunta manualmente. Un modo per farlo sarebbe semplicemente aggiungere il seguente tag <script>
al nostro index.html
:
<script src="bower_components/modernizr/modernizr.js"></script>
In alternativa, possiamo utilizzare il file bower.json
per gestire le nostre dipendenze. Dopo aver seguito attentamente ogni passaggio fino ad ora, il file bower.json
dovrebbe assomigliare a questo:
{ "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 sintassi è abbastanza autoesplicativa, ma ulteriori informazioni sono disponibili qui.
Possiamo quindi aggiungere eventuali nuove dipendenze aggiuntive che desideriamo, quindi tutto ciò di cui abbiamo bisogno è il seguente comando per installarle:
bower install
Ora scriviamo alcuni test!
OK, ora è il momento di riprendere da dove avevamo interrotto nella prima parte e scrivere alcuni test per la nostra app AngularJS.
Ma prima c'è un piccolo problema che dobbiamo affrontare: sebbene gli sviluppatori di generator-angular abbiano basato il loro modello di progetto sul progetto angular-seed (che è il boilerplate ufficiale di Angular), per qualche ragione che non capisco davvero, hanno deciso per modificare le convenzioni di denominazione delle cartelle app
(cambiando css
in styles
, js
in scripts
e così via).

Di conseguenza, l'app che abbiamo originariamente scritto ora ha percorsi che non sono coerenti con lo scaffold che abbiamo appena generato. Per aggirare questo problema, scarichiamo il codice dell'app da qui e lavoriamo con quella versione da questo momento in poi (è per lo più la stessa identica app che abbiamo originariamente scritto, ma con i percorsi aggiornati per corrispondere alla denominazione angolare del generatore).
Dopo aver scaricato l'app, vai alla cartella tests/spec/controllers
e crea un file denominato drivers.js
contenente quanto segue:
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"); }); });
Questa è la suite di test per il nostro driverscontroller
. Può sembrare molto codice, ma la maggior parte di esso è in realtà solo una falsa dichiarazione di dati. Diamo una rapida occhiata agli elementi veramente importanti:
- Il metodo
describe()
definisce la nostra suite di test. - Each
it()
è una specifica di test corretta. - Ogni funzione
beforeEach()
viene eseguita subito prima di ciascuno dei test.
L'elemento più importante (e potenzialmente confuso) qui è il servizio $httpBackend
che abbiamo istanziato sulla variabile httpMock
. Questo servizio funge da falso back-end e risponde alle nostre chiamate API durante le esecuzioni di test, proprio come farebbe il nostro server effettivo in produzione. In questo caso, usando la funzione expectJSONP()
, la impostiamo per intercettare eventuali richieste JSONP all'URL specificato (lo stesso che utilizziamo per ottenere le informazioni dal server) e, invece, restituiamo un elenco statico con tre driver, imitando il risposta del server reale. Questo ci consente di sapere con certezza cosa dovrebbe tornare dal controller. Possiamo quindi confrontare i risultati con quelli attesi, usando la funzione expect()
. Se corrispondono, il test passerà.
L'esecuzione dei test viene eseguita semplicemente con il comando:
grunt test
La suite di test per il controller dei dettagli del driver ( drivercontroller
) dovrebbe essere abbastanza simile a quella che abbiamo appena visto. Ti consiglio di provare a capirlo da solo come esercizio (o puoi semplicemente dare un'occhiata qui, se non sei all'altezza).
Che dire dei test AngularJS end-to-end?
Il team Angular ha recentemente introdotto un nuovo corridore per i test end-to-end chiamato Protractor. Utilizza webdriver per interagire con l'applicazione in esecuzione nel browser e utilizza anche il framework di test Jasmine per impostazione predefinita, quindi la sintassi sarà altamente coerente con quella dei nostri unit test.
Poiché Protractor è uno strumento abbastanza nuovo, tuttavia, la sua integrazione con lo stack Yeoman e il generator-angular
richiede ancora una discreta quantità di lavoro di configurazione. Con questo in mente e la mia intenzione di mantenere questo tutorial il più semplice possibile, il mio piano è di dedicare un post futuro esclusivamente alla copertura approfondita dei test end-to-end in AngularJS.
Conclusione
A questo punto della serie di tutorial, abbiamo imparato come impalcare la nostra app Angular con yo
, gestirne le dipendenze con bower
e scrivere/eseguire alcuni test usando karma
e protractor
. Tieni presente, tuttavia, che questo tutorial è inteso solo come un'introduzione a questi strumenti e pratiche AngularJS; non ne abbiamo analizzato nessuno qui in modo approfondito.
Il nostro obiettivo è stato semplicemente quello di aiutarti a iniziare questo percorso. Da qui, sta a te andare avanti e imparare tutto ciò che puoi su questo straordinario framework e suite di strumenti.
Addendum: Alcune note (importanti) dell'autore
Dopo aver letto questo tutorial, alcune persone potrebbero chiedere "Aspetta. Non dovresti fare tutte queste cose prima di iniziare effettivamente a codificare la tua app? Non avrebbe dovuto far parte di questo tutorial?"
La mia breve risposta è no . Come abbiamo visto nella prima parte, in realtà non è necessario conoscere tutte queste cose per codificare la tua prima app Angular. Piuttosto, la maggior parte degli strumenti che abbiamo discusso in questo post sono progettati per aiutarti a ottimizzare il flusso di lavoro di sviluppo e ad esercitarti con il Test Driven Development (TDD).
E parlando di TDD, il concetto più basilare di TDD è sicuramente valido; vale a dire, scrivi i tuoi test prima di scrivere il tuo codice. Alcune persone, però, portano quel concetto troppo oltre. Il TDD è una pratica di sviluppo, non un metodo di apprendimento. Di conseguenza, scrivere i test prima di scrivere il codice ha molto senso, mentre imparare a scrivere i test prima di imparare a programmare non ha.
Personalmente penso che questo sia il motivo principale per cui i tutorial ufficiali di Angular possono sembrare così contorti e possono essere quasi impossibili da seguire per le persone senza precedenti esperienze MVC/TDD front-end. Questo è uno dei motivi principali per cui ho iniziato questa serie di tutorial.
Il mio consiglio personale per coloro che stanno imparando a navigare nel mondo di AngularJS è: non essere troppo duro con te stesso. Non è necessario imparare tutto in una volta (nonostante le persone ti dicano il contrario!). A seconda della tua precedente esperienza con altri framework front-end/test, AngularJS può essere piuttosto difficile da capire inizialmente. Quindi impara tutto ciò che devi imparare fino a quando non sarai in grado di scrivere le tue semplici app e poi, una volta che avrai dimestichezza con le basi del framework, potrai occuparti di selezionare e applicare le pratiche di sviluppo a lungo termine che funzionano meglio per tu.
Ovviamente, questa è la mia modesta opinione e non tutti saranno d'accordo con quell'approccio (e il team di sviluppo di Angular potrebbe mandarmi dietro un sicario una volta che lo pubblicherò), ma questa è la mia visione e sono abbastanza sicuro che ci siano molte persone là fuori chi sarà d'accordo con me.