Quando ho iniziato a giocare con Ember.js quasi un anno fa, la storia della testabilità lasciava a desiderare. Potresti testare un oggetto senza problemi, ma un test unitario è solo un modo per ottenere feedback quando stai creando un prodotto software. Oltre ai test unitari, volevo un modo per verificare l'integrazione di più componenti. Quindi, come la maggior parte delle persone che testano ricche applicazioni JavaScript, ho raggiunto la madre di tutti gli strumenti di test, il Selenium.
Ora, prima di iniziare, senza una corretta introduzione, vale la pena ricordare che Selenium è un ottimo modo per verificare che l'intera applicazione Web funzioni con un database di produzione completo e tutte le dipendenze di produzione, ecc. E dal punto di vista della QA, questo strumento può essere una grande risorsa per i team che necessitano di test di accettazione dell'interfaccia utente end-to-end.
Ma nel tempo, una suite di test apparentemente piccola costruita su Selenium può iniziare a trascinare la velocità della tua squadra a passo di lumaca. Un modo semplice per ridurre questo dolore è evitare di costruire una grande applicazione in primo luogo. Se invece costruisci una manciata di applicazioni web più piccole, potrebbe aiutarti a rimanere a galla ancora un po 'più a lungo perché nessuna build individuale distruggerà la squadra, man mano che cresci.
Ma anche su un piccolo progetto, il vero problema con Selenium è che non fa parte del processo di sviluppo guidato dai test. Quando sto facendo red / green / refactor non ho tempo per feedback lenti in nessuna forma. Avevo bisogno di un modo per scrivere sia i test di unità e di integrazione che fornirebbero un rapido feedback per aiutarmi a modellare il software che stavo scrivendo in modo più iterativo. Se stai utilizzando una versione di Ember.js> = RC3, sei fortunato perché scrivere un'unità o un test di integrazione è una passeggiata nella parte.
Ora che possiamo scrivere i test JavaScript per la nostra applicazione, come li eseguiamo? La maggior parte degli sviluppatori inizia a utilizzare direttamente il browser, ma poiché volevo qualcosa che potessi eseguire headless dalla riga di comando in un ambiente CI con un ricco ecosistema pieno di plugin, ho cercato Karma.
Quello che mi è piaciuto del Karma è che vuole essere solo il tuo runner di prova. Non interessa quale sia il framework di test JavaScript che usi o quale framework MVC lato client usi. È semplice iniziare e scrivere test che vengono eseguiti sull'applicazione Ember.js di produzione sono solo alcune linee di configurazione.
Ma prima di poter configurare Karma, dobbiamo installarlo usando npm. Raccomando di installarlo localmente in modo da poter mantenere i moduli npm isolati per progetto. Per fare ciò, aggiungi un file chiamato package.json
'alla radice del tuo progetto che assomiglia al seguente.
"dipendenze": "karma-qunit": "*", "karma": "0.10.2"
Questo esempio richiede sia Karma, sia un plugin per QUnit. Dopo aver salvato il file package.json
file sopra, tornare alla riga di comando e digitare installazione di npm
tirare giù i moduli Node richiesti.
Al termine dell'installazione di npm, verrà ora visualizzata una nuova cartella con il nome node_modules
nella radice del tuo progetto. Questa cartella contiene tutto il codice JavaScript appena estratto con npm, incluso Karma e il plugin QUnit. Se approfondisci ulteriormente node_modules / karma / bin /
vedrai l'eseguibile di Karma. Lo useremo per configurare il test runner, eseguire test dalla riga di comando, ecc.
Quindi dobbiamo configurare il karma in modo che sappia come eseguire i test QUnit. genere karma init
dalla radice del progetto. Ti verrà richiesto un elenco di domande. Il primo chiederà quale sia il framework di test che si desidera utilizzare, colpito linguetta finché non vedi qunit
, poi colpisci accedere. Risposta successiva no
alla domanda Require.js, dato che non la useremo per questa applicazione di esempio. linguetta finché non vedi PhantomJS per la terza domanda e dovrai colpire accedere due volte in quanto consente più opzioni qui. Per il resto, basta lasciarli alla loro opzione predefinita.
Quando hai finito, dovresti vedere che Karma ha generato un file di configurazione chiamato karma.conf.js
nella radice o nel tuo progetto. Se vuoi saperne di più sulle varie opzioni supportate da Karma, potresti trovare utili i commenti. Per il bene di questo esempio, ho una versione semplificata del file di configurazione per mantenere le cose amichevoli per i principianti.
Se si desidera seguire, eliminare il file di configurazione generato e sostituirlo con questo.
module.exports = function (karma) karma.set (basePath: 'js', file: ["fornitore / jquery / jquery.min.js", "fornitore / handlebars / handlebars.js", "fornitore / ember / ember.js "," fornitore / jquery-mockjax / jquery.mockjax.js "," app.js "," tests / *. js "], logLevel: karma.LOG_ERROR, browser: ['PhantomJS'], singleRun: true, autoWatch: false, frameworks: ["qunit"]); ;
Questo dovrebbe essere abbastanza simile a quello che Karma ha generato in precedenza, ho appena rimosso tutti i commenti e ritagliato alcune opzioni di cui non ci importa adesso. Per scrivere il primo test di unità, ho dovuto dire a Karma un po 'di più sulla struttura del progetto.
Nella parte superiore del file di configurazione, vedrai che ho impostato il file basePath
a js
perché tutte le risorse JavaScript vivono sotto questa cartella nel progetto. Successivamente, ho detto a Karma dove può trovare i file JavaScript necessari per testare la nostra semplice applicazione. Questo include jQuery, Handlebars, Ember.js e il app.js
file stesso.
Ora possiamo aggiungere il primo file di test dell'unità al progetto. Per prima cosa fai una nuova cartella chiamata test
e annidalo sotto il js
cartella. Aggiungi un file in questa nuova directory chiamata unit_tests.js
sembra qualcosa del genere.
test ('hello world', function () equal (1, 1, ""););
Questo test non sta ancora facendo nulla di utile, ma ci aiuterà a verificare che abbiamo tutto cablato con Karma per eseguirlo correttamente. Notare nel Karma File
sezione, abbiamo già aggiunto il js / prove
directory. In questo modo Karma inserirà in ogni file JavaScript che usiamo per testare la nostra applicazione, andando avanti.
Ora che abbiamo configurato correttamente Karma, esegui i test qunit dalla riga di comando usando ./ node_modules / karma / bin / karma start
.
Se hai configurato tutto correttamente, dovresti vedere Karma eseguire un test e aver successo. Per verificare che abbia eseguito il test che abbiamo appena scritto, fallo fallito modificando l'istruzione equals. Ad esempio, potresti fare quanto segue:
test ('ciao mondo', function () equal (1, 2, "boom"););
Se riesci a fallire e farlo passare di nuovo, è il momento di scrivere un test con un altro scopo.
Ma prima di iniziare, parliamo dell'applicazione di esempio utilizzata in questo post. Nello screenshot qui sotto, si vede che abbiamo una griglia di utenti molto semplice. Nella tabella HTML, ogni utente viene mostrato per nome insieme a un pulsante per eliminare quell'utente. Nella parte superiore dell'applicazione verrà visualizzato un input per il nome, il cognome e infine un pulsante che aggiungerà un altro utente alla tabella quando si fa clic.
https://dl.dropboxusercontent.com/u/716525/content/images/2013/pre-tuts.png
L'applicazione di esempio ha tre problemi. Per prima cosa, vogliamo mostrare il nome e il cognome dell'utente, non solo il nome. Successivamente, quando fai clic su un pulsante di eliminazione, in realtà non rimuoverà l'utente. E infine, quando aggiungi un nome, un cognome e fai clic su aggiungi, non metterà un altro utente nella tabella.
In superficie, la modifica completa del nome sembra essere la più semplice. Si è anche rivelato un ottimo esempio che mostra quando è necessario scrivere un test unitario, un test di integrazione o entrambi. In questo esempio, il modo più rapido per ottenere feedback è scrivere un semplice test unitario che asserisca che il modello ha una proprietà calcolata nome e cognome
.
L'unità testare un oggetto ember è facile, basta creare una nuova istanza dell'oggetto e chiedere il nome e cognome
valore.
test ('fullName restituisce sia il primo che l'ultimo', function () var person = App.Person.create (firstName: 'toran', lastName: 'billups'); var result = person.get ('fullName' ); uguale (risultato, 'toran billups', "fullName was" + result););
Avanti se torni alla riga di comando e corri ./ node_modules / karma / bin / karma start
, dovrebbe mostrare un test fallito con un messaggio utile che descrive nome e cognome
al momento non definito. Per risolvere questo problema, dobbiamo aprire il app.js
file e aggiungi una proprietà calcolata al modello che restituisce una stringa dei valori combinati del nome e del cognome.
App.Person = Ember.Object.extend (firstName: ", lastName:", fullName: function () var firstName = this.get ('firstName'); var lastName = this.get ('lastName'); firstName + "+ lastName; .property ());
Se torni alla riga di comando e corri ./ node_modules / karma / bin / karma start
ora dovresti vedere un test dell'unità che passa. È possibile estendere questo esempio scrivendo alcuni altri test di unità per mostrare che la proprietà calcolata dovrebbe cambiare quando il nome o il cognome viene aggiornato sul modello.
test ('fullName restituisce sia il primo che l'ultimo', function () var person = App.Person.create (firstName: 'toran', lastName: 'billups'); var result = person.get ('fullName' ); uguale (risultato, 'toran billups', "fullName was" + result);); test ('fullName aggiorna la proprietà quando firstName è cambiato', function () var person = App.Person.create (firstName: 'toran', lastName: 'billups'); var result = person.get ('fullName' ) uguale (risultato, 'toran billups', "fullName era" + risultato); person.set ('firstName', 'wat'); result = person.get ('fullName'); uguale (risultato, 'wat billups ', "fullName era" + risultato);); test ('fullName aggiorna la proprietà quando lastName è cambiato', function () var person = App.Person.create (firstName: 'toran', lastName: 'billups'); var result = person.get ('fullName' ) uguale (risultato, 'toran billups', "fullName era" + risultato); person.set ('lastName', 'tbozz'); result = person.get ('fullName'); uguale (risultato, 'toran tbozz ', "fullName era" + risultato););
Se aggiungi questi due test aggiuntivi ed esegui tutti e tre dalla riga di comando, dovresti avere due errori. Per far passare tutti e tre i test, modificare la proprietà calcolata per ascoltare le modifiche sia sul nome che sul cognome. Ora se corri ./ node_modules / karma / bin / karma start
dalla riga di comando, dovresti avere tre test di passaggio.
App.Person = Ember.Object.extend (firstName: ", lastName:", fullName: function () var firstName = this.get ('firstName'); var lastName = this.get ('lastName'); firstName + "+ lastName; .property ('firstName', 'lastName'));
Ora che abbiamo una proprietà calcolata sul modello, dobbiamo guardare il modello stesso perché al momento non usiamo il nuovo nome e cognome
proprietà. In passato, dovresti cablare tutto da solo o utilizzare il selenio per verificare che il modello sia reso correttamente. Ma con i test di ember è ora possibile integrare questo test aggiungendo alcune righe di JavaScript e un plugin per Karma.
Per prima cosa apri il package.json
file e aggiungi la dipendenza karma-ember-preprocessore. Dopo aver aggiornato il package.json
file, fallo installazione di npm
dalla riga di comando per tirarlo giù.
"dipendenze": "karma-ember-preprocessore": "*", "karma-qunit": "*", "karma": "0.10.2"
Ora che hai installato il pre-processore, dobbiamo rendere Karma consapevole dei file modello. Nel File
sezione del tuo karma.conf.js
file aggiungi quanto segue per dire a Karma dei modelli di Handlebars.
module.exports = function (karma) karma.set (basePath: 'js', file: ["fornitore / jquery / jquery.min.js", "fornitore / handlebars / handlebars.js", "fornitore / ember / ember.js "," fornitore / jquery-mockjax / jquery.mockjax.js "," app.js "," tests / *. js "," templates / *. handlebars "], logLevel: karma.LOG_ERROR, browser: ['PhantomJS'], singleRun: true, autoWatch: false, framework: ["qunit"]); ;
Quindi dobbiamo dire a Karma cosa fare con questi file manubri, perché tecnicamente vogliamo che ogni modello sia precompilato prima che venga passato a PhantomJS. Aggiungi la configurazione del preprocessore e punta qualsiasi cosa con un'estensione di file di *.manubrio
al preprocessore ember. Inoltre è necessario aggiungere la configurazione dei plugin per registrare il pre-processore ember (insieme ad alcuni altri che normalmente vengono inclusi nella configurazione predefinita di Karma).
module.exports = function (karma) karma.set (basePath: 'js', file: ["fornitore / jquery / jquery.min.js", "fornitore / handlebars / handlebars.js", "fornitore / ember / ember.js "," fornitore / jquery-mockjax / jquery.mockjax.js "," app.js "," tests / *. js "," templates / *. handlebars "], logLevel: karma.LOG_ERROR, browser: ['PhantomJS'], singleRun: true, autoWatch: false, framework: ["qunit"], plugin: ['karma-qunit', 'karma-chrome-launcher', 'karma-ember-preprocessore', 'karma- phantomjs-launcher '], preprocessors: "** / *. handlebars":' ember '); ;
Ora che abbiamo configurato la configurazione di Karma per i test di integrazione, aggiungi un nuovo file chiamato integration_tests.js
sotto il test
cartella. All'interno di questa cartella abbiamo bisogno di aggiungere un semplice test per dimostrare che possiamo sostenere l'intera applicazione Ember.js senza errori. Aggiungi un semplice test di qunit per vedere se possiamo colpire il '/'
instradare e ottenere l'HTML di base restituito. Per il test iniziale, stiamo solo affermando che il tavolo
il tag esiste nel codice HTML che è stato generato.
test ('hello world', function () App.reset (); visit ("/"). then (function () ok (exists ("table"));););
Si noti che stiamo usando alcuni helper che sono integrati nei test di ember come visita
e trova
. Il visita
helper è un modo amichevole di raccontare l'applicazione in quale stato essere durante l'esecuzione. Questo test inizia dal '/'
instradamento perché è lì che i modelli People vengono associati al modello e viene generata la nostra tabella HTML. Il trova
helper è un modo rapido per cercare elementi nel DOM usando selettori CSS come faresti con jQuery per verificare qualcosa sul markup.
Prima di poter eseguire questo test, è necessario aggiungere un file helper di test che inietti gli helper di test e imposta un elemento radice generico. Aggiungi il codice qui sotto, in un file chiamato integration_test_helper.js
nello stesso test
directory. Ciò garantirà che la nostra applicazione abbia gli helper del test al momento dell'esecuzione.
document.write (''); App.rootElement = '# ember-testing'; App.setupForTesting (); App.injectTestHelpers (); esiste una funzione (selettore) return !! find (selector) .length;
Ora dalla riga di comando dovresti essere in grado di eseguire il test di integrazione sopra. Se hai un test di passaggio, rimuovi la tabella dal modello di manubri per farlo fallire (solo per dimostrare che Ember stava generando l'HTML usando quel modello).
Ora che abbiamo la configurazione dei test di integrazione, è il momento di scrivere quello che afferma che mostriamo ogni utente nome e cognome
invece del loro nome di battesimo
. Vogliamo prima affermare che otteniamo due righe, una per ogni persona.
test ('hello world', function () App.reset (); visit ("/"). then (function () var rows = find ("table tr"). length; uguale (righe, 2, righe );););
Nota: l'applicazione sta attualmente restituendo dati codificati per mantenere tutto semplice al momento. Se sei curioso del perché otteniamo due persone, ecco il trova
metodo sul modello:
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 (first); this.people.pushObject (last); return this.people;);
Se eseguiamo i test ora, dovremmo comunque avere tutto ciò che passa perché due persone vengono restituite come ci aspetteremmo. Successivamente, abbiamo bisogno di ottenere la cella della tabella che mostra il nome della persona e affermare che sta usando il nome e cognome
proprietà invece di solo nome di battesimo
.
test ('hello world', function () App.reset (); visit ("/"). then (function () var rows = find ("table tr"). length; uguale (righe, 2, righe ) var fullName = find ("tabella tr: eq (0) td: eq (0)"). text (); uguale (fullName, "xy", "la prima riga della tabella aveva fullName:" + fullName); ););
Se esegui il test sopra riportato dovresti vedere un test in errore perché non abbiamo ancora aggiornato il modello da utilizzare nome e cognome
. Ora che abbiamo un test non funzionante, aggiorna il modello da utilizzare nome e cognome
ed esegui i test usando ./ node_modules / karma / bin / karma start
. Ora dovresti avere una suite di passaggi sia di unità che di test di integrazione.
Se ti stai chiedendo "quando dovrei scrivere un test unitario contro un test di integrazione?", La risposta è semplicemente: cosa sarà meno doloroso? Se scrivere un test unitario è più veloce e spiega il problema meglio di un test di integrazione molto più ampio, allora dico scrivere il test unitario. Se i test unitari sembrano meno preziosi perché stai facendo il CRUD di base e il comportamento reale è nell'interazione tra i componenti, io dico di scrivere il test di integrazione. Poiché i test di integrazione scritti con il test ember sono incredibilmente veloci, fanno parte del ciclo di feedback degli sviluppatori e dovrebbero essere utilizzati in modo simile a un test unitario quando ha senso.
Per mostrare un test di integrazione simile a CRUD in azione, scrivere il seguente test per provare il Inserisci pulsante mette la persona nella raccolta e una nuova riga viene sottoposta a rendering nel modello di manubri.
test ('aggiungi aggiungerà un'altra persona alla tabella html', function () App.Person.people = []; App.reset (); visit ("/"). then (function () var rows = find ("table tr"). length equal (rows, 2, "the table had" + rows + "rows"); fillIn (". firstName", "foo"); fillIn (". Cognome", "barra") ; return click (". submit");). then (function () equal (find ("table tr"). length, 3, "la tabella delle persone non era completa"); uguale (find ("table tr: eq (2) td: eq (0) "). text ()," foo bar "," il fullName per la persona non era corretto ");););
Inizia dicendo al test su quale stato vuoi lavorare, quindi usa il riempire
helper, aggiungi un nome e un cognome. Ora, se si fa clic sul Sottoscrivi pulsante dovrebbe aggiungere quella persona alla tabella HTML, quindi nel ritorno poi
possiamo affermare che esistono tre persone nella tabella HTML. Esegui questo test e dovrebbe fallire perché il controller Ember non è completo.
Per far passare il test, aggiungere la seguente riga a PeopleController
App.PeopleController = Ember.ArrayController.extend (actions: addPerson: function () var person = firstName: this.get ('firstName'), lastName: this.get ('lastName'); App.Person .add (persona););
Ora se esegui i test usando ./ node_modules / karma / bin / karma start
dovrebbe mostrare tre persone nel codice HTML visualizzato.
L'ultimo test è l'eliminazione, si noti che troviamo il pulsante per una riga specifica e fare clic su di esso. Nel seguente poi
semplicemente verifichiamo che una persona in meno sia mostrata nella tabella HTML.
test ('delete rimuoverà la persona per una determinata riga', function () App.Person.people = []; App.reset (); visit ("/"). then (function () var rows = find ("tabella tr"). length; uguale (righe, 2, "la tabella ha" + rows + "rows"); return click ("table .delete: first");). then (function () equal (trova ("table tr"). length, 1, "la tabella delle persone non era completa););")))
Per ottenere questo passaggio, è sufficiente aggiungere la seguente riga al PeopleController
:
App.PeopleController = Ember.ArrayController.extend (actions: addPerson: function () var person = firstName: this.get ('firstName'), lastName: this.get ('lastName'); App.Person .add (persona);, deletePerson: function (person) App.Person.remove (person););
Esegui i test dalla riga di comando e dovresti nuovamente avere una suite di test di passaggio.
In modo che avvolge la nostra applicazione di esempio. Sentiti libero di porre qualsiasi domanda nei commenti.
Se preferisci usare Grunt invece del preprocessore karma-ember, semplicemente rimuovi la configurazione dei plugin e dei preprocessori. Anche rimuovere templates / *. manubrio
dalla sezione files in quanto Karma non avrà bisogno di precompilare i template. Ecco un semplificato karma.conf.js
che funziona quando si usa grunt per precompilare i modelli di manubri.
module.exports = function (karma) karma.set (basePath: 'js', file: ["lib / deps.min.js", // creato dal tuo task grunt "tests / *. js"], logLevel : karma.LOG_ERROR, browser: ['PhantomJS'], singleRun: true, autoWatch: false, framework: ["qunit"]); ;
E questo è tutto!