Come testare il codice JavaScript con QUnit

QUnit, sviluppato dal team jQuery, è un ottimo framework per testare il tuo codice JavaScript. In questo tutorial, introdurrò ciò che è specificamente QUnit e perché dovresti preoccuparti di testare rigorosamente il tuo codice.

Cos'è QUnit

QUnit è un potente framework di test delle unità JavaScript che ti aiuta a eseguire il debug del codice. È scritto dai membri del team jQuery ed è la suite di test ufficiale per jQuery. Ma QUnit è abbastanza generale da testare qualsiasi codice JavaScript regolare, ed è anche in grado di testare JavaScript sul lato server tramite un motore JavaScript come Rhino o V8.

Se non conosci l'idea di "unit test", non preoccuparti. Non è troppo difficile da capire:

Nella programmazione per computer, il test unitario è un metodo di verifica e convalida del software in cui un programmatore verifica se singole unità del codice sorgente sono idonee all'uso. Un'unità è la più piccola parte testabile di un'applicazione. Nella programmazione procedurale un'unità può essere una singola funzione o procedura.

Questo è citato da Wikipedia. In poche parole, si scrivono test per ciascuna funzionalità del codice e, se tutti questi test vengono superati, si può essere certi che il codice sarà privo di errori (principalmente, dipende da quanto accurati siano i test).

Perché dovresti testare il tuo codice

Se non hai mai scritto alcun test unitario, probabilmente applichi il tuo codice direttamente a un sito web, fai clic per un istante per vedere se si verificano problemi e prova a risolverlo mentre ne individua uno. Ci sono molti problemi con questo metodo.

Innanzitutto, è molto noioso Fare clic in realtà non è un lavoro facile, perché devi assicurarti che tutto sia cliccato ed è molto probabile che ti manchi una cosa o due. In secondo luogo, tutto ciò che hai fatto per il test non è riutilizzabile, il che significa che non è facile trovare regressioni. Cos'è una regressione? Immagina di aver scritto un codice e averlo testato, corretto tutti i bug che hai trovato e pubblicato. Quindi, un utente invia un feedback sui nuovi bug e richiede alcune nuove funzionalità. Torna al codice, correggi questi nuovi bug e aggiungi queste nuove funzionalità. Quello che potrebbe accadere dopo è che alcuni dei vecchi bug si presentano di nuovo, che sono chiamati "regressioni". Vedi, ora devi fare di nuovo il click, e probabilmente non troverai più questi vecchi bug; anche se lo fai, ci vorrà un po 'prima di capire che il problema è causato da regressioni. Con il test delle unità, si scrivono i test per trovare i bug, e una volta che il codice viene modificato, lo si filtra nuovamente attraverso i test. Se appare una regressione, alcuni test saranno sicuramente falliti, e puoi facilmente individuarli, sapendo quale parte del codice contiene il bug. Dal momento che sai cosa hai appena modificato, può essere facilmente risolto.

Un altro vantaggio del test unitario è in particolare per lo sviluppo web: facilita la verifica della compatibilità cross-browser. Basta eseguire i test su diversi browser e, se si verifica un problema su un browser, lo si aggiusta e si eseguono nuovamente questi test, assicurandosi che non introduca la regressione su altri browser. Puoi essere sicuro che tutti i browser di destinazione sono supportati, una volta che tutti superano i test.

Vorrei menzionare uno dei progetti di John Resig: TestSwarm. Rende il testing delle unità JavaScript a un nuovo livello, rendendolo distribuito. È un sito Web che contiene molti test, chiunque può andare lì, eseguire alcuni test, quindi restituire il risultato al server. In questo modo, il codice può essere testato su diversi browser e persino su piattaforme diverse molto velocemente.

Come scrivere i test delle unità con QUnit

Quindi, come si scrivono i test unitari con QUnit esattamente? Innanzitutto, è necessario configurare un ambiente di test:

    QUnit Test Suite         

QUnit Test Suite

    Come puoi vedere, qui viene utilizzata una versione in hosting del framework QUnit.

    Il codice che verrà testato dovrebbe essere inserito in myProject.js e i test dovrebbero essere inseriti in myTests.js. Per eseguire questi test, è sufficiente aprire questo file HTML in un browser. Ora è il momento di scrivere alcuni test.

    Gli elementi costitutivi dei test unitari sono asserzioni.

    Un'affermazione è una dichiarazione che prevede il risultato di ritorno del tuo codice. Se la previsione è falsa, l'asserzione è fallita e tu sai che qualcosa è andato storto.

    Per eseguire asserzioni, devi inserirle in un caso di test:

     // testiamo questa funzione function isEven (val) return val% 2 === 0;  test ('isEven ()', function () ok (isEven (0), 'Zero è un numero pari'); ok (isEven (2), 'So is two'); ok (isEven (-4) , 'Così è negativo quattro'); ok (! IsEven (1), 'Uno non è un numero pari'); ok (! IsEven (-7), 'Né è negativo sette');)

    Qui abbiamo definito una funzione, isEven, che rileva se un numero è pari, e vogliamo testare questa funzione per assicurarci che non restituisca risposte errate.

    Per prima cosa chiamiamo test (), che costruisce un caso di test; il primo parametro è una stringa che verrà visualizzata nel risultato e il secondo parametro è una funzione di callback che contiene le nostre asserzioni. Questa funzione di richiamata verrà richiamata una volta eseguita QUnit.

    Abbiamo scritto cinque asserzioni, tutte booleane. Un'asserzione booleana prevede che il suo primo parametro sia vero. Il secondo parametro è anche un messaggio che verrà visualizzato nel risultato.

    Ecco cosa ottieni, una volta eseguito il test:

    Dal momento che tutte queste asserzioni sono state approvate con successo, possiamo esserne certi è anche() funzionerà come previsto.

    Vediamo cosa succede se un'affermazione è fallita.

     // testiamo questa funzione function isEven (val) return val% 2 === 0;  test ('isEven ()', function () ok (isEven (0), 'Zero è un numero pari'); ok (isEven (2), 'So is two'); ok (isEven (-4) , 'Così è negativo quattro'); ok (! IsEven (1), 'Uno non è un numero pari'); ok (! IsEven (-7), 'Né fa negativo sette'); // Fallisce ok (isEven (3), "Tre è un numero pari");)

    Ecco il risultato:

    L'asserzione è fallita perché abbiamo deliberatamente scritto male, ma nel tuo progetto, se il test non è passato e tutte le asserzioni sono corrette, sai che è stato trovato un bug.

    Altre asserzioni

    ok () non è l'unica affermazione fornita da QUnit. Ci sono altri tipi di asserzioni che sono utili quando collaudi il tuo progetto:

    Asserzione di confronto

    L'asserzione di confronto, equals (), si aspetta che il suo primo parametro (che è il valore attuale) sia uguale al suo secondo parametro (che è il valore atteso). È simile a ok (), ma restituisce valori effettivi e attesi, rendendo molto più facile il debug. Come ok (), richiede un terzo parametro opzionale come messaggio da visualizzare.

    Quindi, invece di:

     test ('assertions', function () ok (1 == 1, 'one equals one');)

    Dovresti scrivere:

     test ('assertions', function () equals (1, 1, 'one equals one');)

    Si noti l'ultimo "1", che è il valore di confronto.

    E se i valori non sono uguali:

     test ('assertions', function () equals (2, 1, 'one equals one');)

    Fornisce molte più informazioni, rendendo la vita molto più semplice.

    L'asserzione di confronto utilizza "==" per confrontare i suoi parametri, quindi non gestisce il confronto tra array o oggetto:

     test ('test', function () equals (, , 'fail, questi sono oggetti diversi'); equals (a: 1, a: 1, 'fail'); equals ([ ], [], 'fallisce, ci sono matrici diverse'); equals ([1], [1], 'fail');)

    Per testare questo tipo di uguaglianza, QUnit fornisce un altro tipo di affermazione: affermazione identica.

    Asserzione identica

    Asserzione identica, same (), si aspetta gli stessi parametri di equals (), ma è un'asserzione di confronto ricorsivo profonda che funziona non solo su tipi primitivi, ma anche su matrici e oggetti. Le asserzioni, nell'esempio precedente, passeranno tutte se le si modificano in asserzioni identiche:

     test ('test', function () same (, , 'passa, oggetti hanno lo stesso contenuto'); same (a: 1, a: 1, 'passa'); same ( [], [], 'passa, gli array hanno lo stesso contenuto'); same ([1], [1], 'passa');)

    Si noti che same () usa '===' per fare confronti quando possibile, quindi sarà utile quando si confrontano valori speciali:

     test ('test', function () equals (0, false, 'true'); same (0, false, 'false'); equals (null, undefined, 'true'); same (null, undefined, ' falso ');)

    Struttura le tue asserzioni

    Mettere tutte le asserzioni in un singolo caso di test è una pessima idea, perché è molto difficile da mantenere e non restituisce un risultato pulito. Quello che dovresti fare è strutturarli, metterli in diversi casi di test, ognuno dei quali mira a una singola funzionalità.

    È anche possibile organizzare casi di test in diversi moduli chiamando la funzione del modulo:

     modulo ('Modulo A'); test ('a test', function () ); test ('un altro test', function () ); modulo ('Modulo B'); test ('a test', function () ); test ('un altro test', function () );

    Test asincrono

    Negli esempi precedenti, tutte le asserzioni sono chiamate in modo sincrono, il che significa che vengono eseguite una dopo l'altra. Nel mondo reale ci sono anche molte funzioni asincrone, come le chiamate o le funzioni ajax chiamate da setTimeout () e setInterval (). Come possiamo testare questo tipo di funzioni? QUnit offre un tipo di test specifico chiamato "test asincrono", dedicato ai test asincroni:

    Proviamo prima a scriverlo in modo regolare:

     test ('test asincrono', function () setTimeout (function () ok (true);, 100))

    Vedere? È come se non avessimo scritto alcuna asserzione. Questo perché l'asserzione ha funzionato in modo asincrono, al momento in cui è stato chiamato, il test case era già finito.

    Ecco la versione corretta:

     test ('test asincrono', function () // Metti in pausa il test first stop (); setTimeout (function () ok (true); // Dopo che l'asserzione è stata chiamata, // continua il test start (); , 100))

    Qui, usiamo stop () per mettere in pausa il caso di test, e dopo che l'asserzione è stata chiamata, usiamo start () per continuare.

    Chiamare stop () immediatamente dopo aver chiamato test () è abbastanza comune; quindi QUnit fornisce una scorciatoia: asyncTest (). Puoi riscrivere l'esempio precedente in questo modo:

     asyncTest ('test asincrono', function () // Il test viene automaticamente messo in pausa setTimeout (function () ok (true); // Dopo che l'asserzione è stata chiamata, // continua il test start ();, 100 ))

    C'è una cosa a cui prestare attenzione: setTimeout () chiamerà sempre la sua funzione di callback, ma cosa succede se si tratta di una funzione personalizzata (ad esempio una chiamata Ajax). Come puoi essere sicuro che la funzione di callback sarà chiamata? E se il callback non viene chiamato, start () non verrà chiamato e l'intero test dell'unità si bloccherà:

    Quindi ecco cosa fai:

     // Una funzione di funzione personalizzata ajax (successCallback) $ .ajax (url: 'server.php', success: successCallback);  test ('test asincrono', function () // Sospende il test e fallisce se start () non viene chiamato dopo un secondo stop (1000); ajax (function () // ... assertion asincrone start ( );))

    Si passa un timeout a stop (), che dice a QUnit, "se start () non viene chiamato dopo quel timeout, si dovrebbe fallire questo test." Puoi star sicuro che l'intero test non si bloccherà e ti verrà notificato se qualcosa va storto.

    Che ne dici di più funzioni asincrone? Dove metti lo start ()? Lo metti in setTimeout ():

     // Una funzione di funzione personalizzata ajax (successCallback) $ .ajax (url: 'server.php', success: successCallback);  test ('test asincrono', function () // Sospende il test stop (); ajax (function () // ... asincroni assertions) ajax (function () // ... asincroni assertions) setTimeout (function () start ();, 2000);)

    Il timeout dovrebbe essere abbastanza lungo da permettere di richiamare entrambi i callback prima che il test continui. Ma cosa succede se uno dei callback non viene chiamato? Come puoi saperlo? È qui che si aspetta ():

     // Una funzione di funzione personalizzata ajax (successCallback) $ .ajax (url: 'server.php', success: successCallback);  test ('test asincrono', function () // Metti in pausa il test stop (); // Dì a QUnit che ti aspetti di eseguire tre asserzioni expect (3); ajax (function () ok (true);) ajax (function () ok (true); ok (true);) setTimeout (function () start ();, 2000);)

    Passi in un numero da aspettarsi () per dire a QUnit che ti aspetti X molte asserzioni da eseguire, se una delle asserzioni non viene chiamata, il numero non corrisponde, e ti verrà notificato che qualcosa è andato storto.

    C'è anche una scorciatoia per expect (): basta passare il numero come secondo parametro da test () o asyncTest ():

     // Una funzione di funzione personalizzata ajax (successCallback) $ .ajax (url: 'server.php', success: successCallback);  // Indica a QUnit che ti aspetti di eseguire tre test ('test asincrono', 3, function () // Sospendi il test stop (); ajax (function () ok (true);) ajax (function () ok (true); ok (true);) setTimeout (function () start ();, 2000);)

    Conclusione

    Questo è tutto ciò che devi sapere per iniziare witih QUnit. Il test delle unità è un ottimo metodo per testare il codice prima di pubblicarlo. Se non hai ancora scritto alcun test unitario, è ora di iniziare! Grazie per aver letto!

    • Seguici su Twitter o iscriviti al feed Nettuts + RSS per i migliori tutorial di sviluppo web sul web.