Test di JavaScript da zero

Questo probabilmente non è il primo tutorial sui test che tu abbia mai visto. Ma forse hai avuto i tuoi dubbi sui test e non hai mai avuto il tempo di leggerli. Dopotutto, può sembrare un lavoro extra senza motivo.

Questo tutorial intende cambiare le tue opinioni. Cominceremo dall'inizio: cosa sta testando e perché dovresti farlo? Quindi, parleremo brevemente della scrittura di un codice testabile, prima che in realtà, come sapete, si faccia qualche test! Andiamo ad esso!


Preferisci uno Screencast?

Parte 1


Parte 2


Parte 3



Definizione dei test

Abbastanza semplice, analisi è l'idea di avere una serie di requisiti che un dato pezzo di codice deve essere sufficientemente robusto per essere usato nel mondo reale. Spesso, scriveremo qualche JavaScript e poi lo apriremo nel browser e faremo un po 'di clic per assicurarci che tutto funzioni come ci aspetteremmo. Anche se a volte è necessario, non è il tipo di test di cui stiamo parlando qui. In effetti, spero che questo tutorial ti convinca che il self test rapido e sporco dovrebbe integrare una procedura di test più rigida: l'autodiagnosi va bene, ma un elenco completo di requisiti è fondamentale.


Motivi per i test

Come puoi immaginare, il problema con il test di aggiornamento e clic su JavaScript è duplice:

  1. Potremmo non ricordarci di controllare qualcosa; anche se lo facciamo, potremmo non ricontrollare dopo le modifiche al codice.
  2. Potrebbero esserci alcune parti del codice che non sono realmente verificabili in questo modo.

Scrivendo test che controlli tutto ciò che il tuo codice dovrebbe fare, puoi verificare che il tuo codice sia nel migliore stato di forma prima di usarlo su un sito web. Nel momento in cui qualcosa è effettivamente in esecuzione in un browser, ci sono probabilmente più punti di errore. I test di scrittura consentono di concentrarsi su ogni singola parte testabile; se ogni pezzo fa bene il suo lavoro, le cose dovrebbero funzionare insieme senza problemi (testare singole parti come questa viene chiamata unit test).


Scrittura di codice verificabile

Se sei un programmatore poliglotta), avresti potuto fare test in altre lingue. Ma ho trovato test in JavaScript una bestia diversa da uccidere. Dopo tutto, non stai costruendo troppe interfacce utente, ad esempio, in PHP o Ruby. Spesso, stiamo facendo DOM in JavaScript, e in che modo lo testate esattamente?

Bene, il lavoro DOM non è quello per cui vuoi scrivere i test; è la logica. Ovviamente, quindi, la chiave qui è separare la tua logica e il tuo codice UI. Questo non è sempre facile; Ho scritto la mia giusta dose di interfaccia utente basata su jQuery, e può diventare piuttosto complicato abbastanza rapidamente. Ciò non solo rende difficile il test, ma anche la logica intrecciata e il codice UI possono essere difficili da modificare quando cambia il comportamento desiderato. Ho trovato che usare metodologie come template (anche template) e pub / sub (anche, pub / sub) rendono la scrittura migliore, un codice più testabile più facile.

Ancora una cosa, prima di iniziare a programmare: come scriviamo i nostri test? Esistono numerose librerie di test che puoi usare (e molti buoni tutorial per insegnarti ad usarle, vedi i link come la fine). Tuttavia, costruiremo una piccola libreria di test da zero. Non sarà così elegante come alcune librerie, ma vedrai esattamente cosa sta succedendo.

Con questo in mente, mettiamoci al lavoro!


Costruire un mini quadro di prova

Stiamo costruendo una micro galleria fotografica: una semplice lista di miniature, con un'immagine che mostra a grandezza naturale sopra di loro. Ma prima, costruiamo la funzione di test.

Man mano che si impara di più sulle test e sulle librerie di test, troverete numerosi metodi di test per testare ogni sorta di specifica. Tuttavia, tutto può essere ridotto al fatto che due cose siano uguali o meno: per esempio,

  • Il valore restituito da questa funzione è uguale a quello che ci aspettavamo di recuperare?
  • È questa variabile del tipo che ci aspettavamo che fosse?
  • Questo array ha il numero previsto di elementi?

Quindi, ecco il nostro metodo per testare l'uguaglianza:

var TEST = areEqual: function (a, b, msg) var result = (a === b); console.log ((result? "PASS:": "FAIL:") + msg); ritorno risultato; ;

È piuttosto semplice: il metodo richiede tre parametri. I primi due vengono confrontati e, se sono uguali, i test passano. Il terzo parametro è un messaggio che descrive il test. In questa semplice libreria di test, stiamo semplicemente inviando i nostri test alla console, ma puoi creare output HTML con uno stile CSS appropriato se desideri.

Ecco il areNotEqual metodo (all'interno della stessa TEST oggetto):

areNotEqual: function (a, b, msg) var result = (a! == b); console.log ((result? "PASS:": "FAIL:") + msg); ritorno risultato; 

Noterai le ultime due righe di sono uguali e areNotEqual lo stesso. Quindi, possiamo tirarli fuori in questo modo:

var TEST = areEqual: function (a, b, msg) return this._output (a === b, msg), areNotEqual: function (a, b, msg) return this._output (a! == b, msg); , _output: function (result, msg) console [risultato? "log": "warn"] ((result? "PASS:": "FAIL:") + msg); ritorno risultato; ;

Grande! La cosa bella qui è che possiamo aggiungere altri metodi "zucchero" usando i metodi che abbiamo già scritto:

TEST.isTypeOf = function (obj, type, msg) return this.areEqual (typeof obj, type, msg); ; TEST.isAnInstanceOf = function (obj, type, msg) return this._output (obj instanceof type, msg);  TEST.isGreaterThan = function (val, min, msg) return this._output (val> min, msg); 

Puoi sperimentare da solo; dopo aver seguito questo tutorial, avrai una buona idea di come usarlo.


Preparazione per la nostra galleria

Quindi, creiamo una galleria fotografica super-semplice, usando il nostro mini TEST quadro per creare alcuni test. Qui citerò che, sebbene lo sviluppo basato sui test sia una buona pratica, non lo useremo in questo tutorial, principalmente perché non è qualcosa che puoi imparare in un singolo tutorial; ci vuole molta pratica per davvero Grok. Quando inizi, è più semplice scrivere un po 'di codice e testarlo.

Quindi iniziamo. Certo, avremo bisogno di un codice HTML per la nostra galleria. Manterremo questo piuttosto semplice:

     Test in JavaScript     

Ci sono due cose principali che vale la pena notare qui: innanzitutto, abbiamo un

che contiene il markup molto semplice per la nostra galleria di immagini. No, probabilmente non è molto robusto, ma ci dà qualcosa su cui lavorare. Quindi, nota che stiamo collegando tre >s: uno è la nostra piccola libreria di test, come sopra. Uno è la galleria che creeremo. L'ultimo contiene i test per la nostra galleria. Notare anche i percorsi delle immagini: i nomi dei file di anteprima hanno "-thumb" aggiunto a loro. È così che troveremo la versione più grande dell'immagine.

So che hai voglia di prendere il codice, quindi butta questo in a gallery.css file:

.galleria background: #ececec; overflow: nascosto; larghezza: 620 px;  .gallery> img margin: 20px 20px 0; padding: 0;  .gallery ul list-style-type: none; margine: 20px; padding: 0; overflow: hidden;  .gallery li float: left; margine: 0 10px;  .gallery li: first-of-type margin-left: 0px;  .gallery li: last-of-type margin-right: 0px; 

Ora puoi caricarlo nel tuo browser e vedere qualcosa di simile a questo:

OK GIÀ! Scriviamo un po 'di JavaScript, dobbiamo?


Delineare la nostra galleria

Aprilo gallery.js file. Ecco come iniziamo:

var Gallery = (function () var Gallery = , galleryPrototype = ; Gallery.create = function (id) var gal = Object.create (galleryPrototype); return gal;; return Gallery; ()) ;

Aggiungeremo di più, ma questo è un buon inizio. Abbiamo usato una funzione anonima autoinviante (o un'espressione di funzione immediatamente invocata) per tenere tutto insieme. Il nostro "interno" Galleria la variabile sarà restituita e sarà il valore del nostro pubblico Galleria variabile. Come puoi vedere, chiamando Gallery.create creerà un nuovo oggetto galleria con Object.create. Se non hai familiarità con Object.create, crea semplicemente un nuovo oggetto usando l'oggetto che lo passi come prototipo del nuovo oggetto (è anche abbastanza compatibile con il browser). Inseriremo quel prototipo e aggiungeremo al nostro Gallery.create metodo pure. Ma ora scriviamo il nostro primo test:

var gal = Gallery.create ("gal-1"); TEST.areEqual (typeof gal, "object", "Gallery dovrebbe essere un oggetto");

Iniziamo creando una "istanza" di Galleria; quindi, eseguiamo un test per vedere se il valore restituito è un oggetto.

Metti queste due linee nel nostro galleria-test.js; ora, apri il nostro index.html pagina in un browser e apri una console JavaScript. Dovresti vedere qualcosa di simile a questo:


Grande! Il nostro primo test sta passando!


Scrivere il costruttore

Successivamente, compileremo il nostro Gallery.create metodo. Come vedrai, non ci preoccupiamo di rendere questo codice di esempio super-robusto, quindi utilizzeremo alcune cose che non sono compatibili in tutti i browser mai creati. cioè, document.querySelector / document.querySelectorAll; inoltre, utilizzeremo solo la gestione degli eventi del browser. Sentiti libero di sostituire la tua libreria preferita, se lo desideri.

Quindi, iniziamo con alcune dichiarazioni:

var gal = Object.create (galleryPrototype), ul, i = 0, len; gal.el = document.getElementById (id); ul = gal.el.querySelector ("ul"); gal.imgs = gal.el.querySelectorAll ("ul li img"); gal.displayImage = gal.el.querySelector ("img: first-child"); gal.idx = 0; gal.going = false; gal.ids = [];

Quattro variabili: in particolare, il nostro oggetto galleria e la galleria

    nodo (lo useremo io e len in un minuto). Quindi, sei proprietà sul nostro ragazza oggetto:

    • gal.el è il nodo "root" sul markyp della nostra galleria.
    • gal.imgs è una lista di
    • s che tengono le nostre miniature.
    • gal.displayImage è l'immagine grande nella nostra galleria.
    • gal.idx è l'indice, l'immagine corrente viene visualizzata.
    • gal.going è un booleano: è vero se la galleria è in bicicletta attraverso le immagini.
    • gal.ids sarà una lista degli ID per le immagini nella nostra galleria. Ad esempio, se la miniatura è denominata "dog-thumb.jpg", quindi "cane" è l'ID e "dog.jpg" è l'immagine della dimensione dello schermo.

    Si noti che gli elementi DOM hanno querySelector e querySelectorAll metodi pure. Possiamo usare gal.el.querySelector per assicurarci di selezionare solo elementi all'interno del markup di questa galleria.

    Ora, riempi gal.ids con gli ID immagine:

    len = gal.imgs.length; per (; i < len; i++ )  gal.ids[i] = gal.imgs[i].getAttribute("src").split("-thumb")[0].split("/")[1]; 

    Piuttosto semplice, giusto?

    Infine, colleghiamo un gestore di eventi al

      .

      ul.addEventListener ("click", function (e) var i = [] .indexOf.call (gal.imgs, e.target); if (i> -1) gal.set (i);, false);

      Iniziamo controllando per vedere se l'elemento più basso che ha ricevuto il clic (e.target; non ci stiamo preoccupando del supporto oldie qui) è nella nostra lista di immagini; da NodeLists non hanno un indice di metodo, useremo la versione dell'array (se non hai familiarità con chiamata e applicare in JavaScript, vedere il nostro suggerimento rapido su questo argomento.). Se questo è maggiore di -1, lo passeremo a gal.set. Non abbiamo ancora scritto questo metodo, ma ci arriveremo.

      Ora, torniamo al nostro galleria-test.js file e scrivere alcuni test per assicurarsi che il nostro Galleria l'istanza ha le proprietà giuste:

      TEST.areEqual (gal.el.id, "gal-1", "Gallery.el dovrebbe essere quello specificato"); TEST.areEqual (gal.idx, 0, "L'indice della galleria dovrebbe iniziare da zero");

      Il nostro primo test verifica che il nostro costruttore di gallerie abbia trovato l'elemento giusto. Il secondo test verifica che l'indice inizi a 0. Probabilmente potresti scrivere una serie di test per verificare che abbiamo le proprietà giuste, ma scriveremo test per i metodi che useranno queste proprietà, quindi non sarà così essere necessario.


      Costruire il prototipo

      Ora, torniamo indietro a quello galleryPrototype oggetto che è attualmente vuoto. Qui è dove alloggeremo tutti i metodi del nostro Galleria "Istanze". Iniziamo con impostato metodo: questo è il metodo più impor- tante, perché è quello che effettivamente cambia l'immagine visualizzata. Prende l'indice dell'immagine o la stringa di identificazione dell'immagine.

      // all'interno di 'galleryProtytype' set: function (i) if (typeof i === 'string') i = this.ids.indexOf (i);  this.displayImage.setAttribute ("src", "images /" + this.ids [i] + ".jpg"); return (this.idx = i); 

      Se il metodo ottiene la stringa ID, troverà il numero di indice corretto per quell'ID. Quindi, impostiamo il displayImage'S src al corretto percorso dell'immagine e restituisce il nuovo indice corrente mentre lo imposta come indice corrente.

      Ora testiamo questo metodo (di nuovo galleria-test.js):

      TEST.areEqual (gal.set (4), 4, "Gallery.set (con numero) dovrebbe restituire lo stesso numero passato in"); TEST.areEqual (gal.displayImage.getAttribute ("src"), "images_24 / javascript-testing-from-scratch.jpg", "Gallery.set (con numero) dovrebbe cambiare l'immagine visualizzata"); TEST.areEqual (gal.set ("post"), 3, "Gallery.set (con stringa) dovrebbe passare all'immagine appropriata"); TEST.areEqual (gal.displayImage.getAttribute ("src"), "images / post.jpg", "Gallery.set (con stringa) dovrebbe cambiare le immagini visualizzate");

      Testiamo il nostro metodo di test con un numero e un parametro di stringa per impostato. In questo caso, siamo in grado di controllare il src per l'immagine e assicurarsi che l'interfaccia utente sia regolata di conseguenza; non è sempre possibile o necessario assicurarsi che ciò che l'utente sta vedendo stia rispondendo in modo appropriato (senza usare qualcosa del genere); è qui che il tipo di test di click-around è utile. Tuttavia, possiamo farlo qui, quindi lo faremo.

      Quei test dovrebbero passare. Dovresti anche essere in grado di fare clic sulle miniature e visualizzare le versioni più grandi. Avere un bell'aspetto!

      Quindi, passiamo ad alcuni metodi che si muovono tra le immagini. Questi potrebbero essere utili se si desidera avere i pulsanti "successivo" e "precedente" per scorrere le immagini (non avremo questi pulsanti, ma inseriremo i metodi di supporto).

      Per la maggior parte, passare alle immagini successive e precedenti non è una cosa difficile. Le parti difficili stanno andando alla prossima immagine quando sei all'ultimo o il precedente quando sei al primo.

      // inside 'galleryPrototype' next: function () if (this.idx === this.imgs.length - 1) return this.set (0);  return this.set (this.idx + 1); , prev: function () if (this.idx === 0) return this.set (this.imgs.length - 1);  return this.set (this.idx - 1); , curr: function () return this.idx; ,

      Ok, quindi in realtà non è troppo complicato. Entrambi questi metodi sono metodi "zucchero" per l'utilizzo impostato. Se siamo all'ultima immagine (this.idx === this.imgs.length -1), noi set (0). Se siamo al primo (this.idx === 0), noi set (this.imgs.length -1). Altrimenti, basta aggiungere o sottrarre uno dall'indice corrente. Non dimenticare che stiamo restituendo esattamente ciò che viene restituito dal impostato chiamata.

      Abbiamo anche il curr metodo, anche. Non è affatto complicato: restituisce solo l'indice corrente. Lo testeremo un po 'più tardi.

      Quindi, testiamo questi metodi.

      TEST.areEqual (gal.next (), 4, "Galleria dovrebbe avanzare su .next ()"); TEST.areEqual (gal.prev (), 3, "Gallery dovrebbe tornare su .prev ()");

      Questi vengono dopo i nostri test precedenti, quindi 4 e 3 sono i valori che ci aspetteremmo. E passano!

      C'è rimasto solo un pezzo: è il ciclo automatico delle foto. Vogliamo essere in grado di chiamare gal.start () giocando con le immagini. Certo, saranno a gal.stop () metodo.

      // all'interno di 'galleryPrototype' start: function (time) var thiz = this; tempo = tempo || 3000; this.interval = setInterval (function () thiz.next ();, time); this.going = true; ritorna vero; , stop: function () clearInterval (this.interval); this.going = false; ritorna vero; ,

      Nostro inizio metodo prenderà parametro: il numero di millisecondi che viene visualizzata un'immagine; se non viene fornito alcun parametro, viene impostato automaticamente su 3000 (3 secondi). Quindi, usiamo semplicemente setInterval su una funzione che chiamerà Il prossimo al momento opportuno. Certo, non possiamo dimenticare di impostare this.going a vero. Alla fine, torniamo vero.

      Stop non è troppo difficile Poiché abbiamo salvato l'intervallo come this.interval, possiamo usare clearInterval per finirlo. Quindi, abbiamo impostato this.going per falso e restituire vero.

      Avremo due comode funzioni per sapere se la galleria è in loop:

      isGoing: function () return this.going; , isStopped: function () return! this.going; 

      Ora testiamo questa funzionalità.

      gal.set (0); TEST.areEqual (gal.start (), true, "La galleria dovrebbe essere in loop"); TEST.areEqual (gal.curr (), 0, "L'indice dell'immagine corrente deve essere 0"); setTimeout (function () TEST.areEqual (gal.curr (), 1, "Current image index dovrebbe essere 1"); TEST.areEqual (gal.isGoing (), true, "Gallery dovrebbe andare"); TEST. areEqual (gal.stop (), true, "Gallery should be stopped"); setTimeout (function () TEST.areEqual (gal.curr (), 1, "Current image dovrebbe essere ancora 1"); TEST.areEqual ( gal.isStopped (), true, "La galleria dovrebbe essere ancora ferma");, 3050);, 3050);

      È un po 'più complicato delle nostre precedenti serie di test: iniziamo usando gal.set (0) per assicurarci che iniziamo dall'inizio. Quindi, chiamiamo gal.start () per iniziare il ciclo. Successivamente, lo testiamo gal.curr () restituisce 0, il che significa che stiamo ancora visualizzando la prima immagine. Ora useremo a setTimeout attendere 3050 ms (poco più di 3 secondi) prima di continuare i test. Dentro quello setTimeout, faremo un altro gal.curr (); l'indice dovrebbe ora essere 1. Quindi, lo testeremo gal.isGoing () è vero. Quindi, interromperemo la galleria gal.stop (). Ora ne usiamo un altro setTimeout aspettare un altro quasi 3 secondi; se la galleria si è veramente fermata, l'immagine non sarà in loop, quindi gal.curr () dovrebbe essere ancora 1; questo è ciò che testiamo all'interno del timeout. Finalmente, ci assicuriamo il nostro isStopped il metodo sta funzionando.

      Se questi test sono passati, congratulazioni! Abbiamo completato il nostro Galleria e i suoi test.


      Conclusione

      Se non hai ancora provato i test, spero che tu abbia visto quanto semplice possa essere il test in JavaScript. Come ho detto all'inizio di questo tutorial, è probabile che un buon test richieda di scrivere il tuo JavaScript in un modo leggermente diverso da quello a cui potresti essere abituato. Tuttavia, ho trovato che JavaScript facilmente verificabile è anche JavaScript facilmente gestibile.

      Ti lascerò con diversi link che potresti trovare utili man mano che procedi e scrivi buoni JavaScript e buoni test.

      • QUnit - una unit test suite (Tutorial su QUnit)
      • Jasmine - BDD framework (Tutorial su Jasmine)
      • Test di JavaScript con Assert
      • Test Driven JavaScript (libro) (capitolo di esempio)