Ci sono fasi del ciclo di vita di un'applicazione che ogni sviluppatore vorrebbe evitare; fra queste spiccano la documentazione del codice e la realizzazione di test suite.
Fra le ragioni che spingono gli sviluppatori a odiare queste fasi c'è sicuramente la noiosità del compito ed il tempo non indifferente che di solito vi si deve dedicare.
In questo articolo prenderemo in considerazione la realizzazione di script di test automatizzati (o test suite) per testare le applicazioni JavaScript. Per questo linguaggio, infatti, sono spesso gli stessi sviluppatori a non ritenere necessaria la realizzazione di test suite, salvo poi ritrovarsi a testare manualmente intere interfacce su piattaforme e browser diversi.
Cosa sono le test suite
Con il termine test suite si possono identificare metodologie e tipologie di test molto diverse tra loro per strumenti e finalità. Tuttavia, in generale, ci si riferisce ad una serie di script che vengono lanciati in maniera più o meno automatica e che servono a testare il codice sorgente di un'applicazione sia a livello di metodi e funzioni che di interazione utente, il tutto all'interno di un ambiente specifico separato dal contesto di produzione e fruizione dell'applicazione.
Perché testare le applicazioni
Avere a disposizione uno strumento rapido per la verifica del codice sorgente permette di prevenire alcuni problemi tipici dello sviluppo di applicazioni JavaScript come:
- errori introdotti nella varie iterazioni dello sviluppo come il refactoring;
- impossibilità di verificare la reazione del programma ad input errati dell'utente;
- inconsistenze legate all'aggiunta di nuove funzionalità;
- incompatibilità legate all'aggiornamento di librerie e script di terze parti.
Infine, in un ambiente di sviluppo condiviso, testare il nuovo codice prima di caricarlo su un server garantisce un'elevata affidabilità del codice e permette di evitare lunghe sessioni finali di debug.
Oltre a questi vantaggi generali, le test suite per JavaScript offrono anche la possibilità di verificare velocemente la funzionalità delle applicazioni su diversi browser e diverse piattaforme, in modo da garantire una qualità e solidità difficilmente raggiungibili con i test manuali.
Una questione di tempo
Esistono varie teorie relative alle tempistiche e alle modalità con le quali effettuare i test di un'applicazione.
L'approccio più intuitivo è quello di scrivere i test e lanciarli durante la fase di debug del codice.
Il problema è che questa metodologia rende il lavoro molto lungo e spesso noioso, costringendo lo sviluppatore a ripercorrere codice scritto magari molto tempo prima.
All'opposto, è possibile creare e riadattare i test ad ogni fase di modifica del codice, per poi lanciarli e verificarne il funzionamento. In questo caso si è sempre sicuri che il codice prodotto sia stabile ed è molto più facile isolare gli errori introdotti con le ultime modifiche.
Un approccio diverso è invece quello del Test Driven Development (TDD), nel quale lo sviluppatore realizza prima il test (in modo che fallisca) per poi scrivere il codice che lo faccia verificare.
Il livello di stabilità garantito da questa metodologia di sviluppo è molto alto, anche se ad un primo contatto può sembrare poco chiara.
Tipologie di test suite in JavaScript
In ambito JavaScript una test suite può avvalersi principalmente di tre strumenti in base al livello di complessità dell'applicazione, della sua interazione con il DOM e della necessità di verifiche cross-browser.
- Unit Testing
- UI Testing
- Test Automation / Distributed Testing
Unit Testing
Questa metodologia di test, comune ad altri linguaggi informatici, prevede la realizzazione di un test per ogni singola unità di codice. In JavaScript un'unità è rappresentata da ogni funzione dello script o dai metodi di un oggetto.
Per la realizzazione di una test suite si definiscono dei test, ognuno dei quali conterrà una lista di affermazioni (assertion) che verificheranno o meno (restituendo true
o false
) la corretta funzionalità dell'unità.
Uno dei primi strumenti dedicati allo Unit Testing JavaScript è stato JSUnit, una derivazione di JUnit per Java. Tuttavia, da qualche tempo il progetto è stato abbandonato ed è quindi meglio far riferimento ad altri strumenti con un supporto ancora attivo.
Al momento il progetto più seguito è QUnit, sviluppato dal team di jQuery come motore di test interno e quindi rilasciato come libreria standalone.
Come per jQuery, il vantaggio principale di QUnit è la semplicità della sintassi. Eccone un esempio:
test("Test di una funzione che verifica valori 'falsy'", function() {
expect(3);
function isFalsy(obj) {
return obj == false;
}
ok(isFalsy(''), "Una stringa vuota è valutata a false");
ok(isFalsy(0), "Il numero 0 è valutato a false");
equal(isFalsy(null), false, "null è falsy, tuttavia è uguale solo a sé stesso e undefined");
});
Nel codice qui sopra abbiamo definito un test per verificare la funzionalià di una semplice funzione isFalsy
, il cui scopo è verificare come attraverso l'operatore ==
molti valori diversi da false
siano in realtà valutati come false
(per effetto del type coercion).
All'interno del test abbiamo utilizzato la funzione ok
, che verifica l'assertion quando il primo argomento è true
, utilizzando il secondo come commento, mentre equal
accetta confronta il valore dei primi due argomenti. Ecco il test in azione.
Da notare la presenza di expect(3)
: con questa funzione indichiamo che il test, per essere completato con successo, deve presentare tre assertion verificate.
UI Testing
Il limite della metodologia Unit Testing risiede nel fatto che in JavaScript buona parte dell'applicazione gestisce manipolazioni del DOM, degli stili CSS, eventi di varia natura (mouse, tastiera), nonché chiamate asincrone.
A questo problema rispondono librerie e strumenti specifici che permettono, a vari livelli, di ricreare un ambiente interattivo in grado di fornire oggetti e interfacce per i nostri test.
Un primo livello di simulazione viene coperto da alcune librerie in grado di simulare oggetti JavaScript (comprese risorse AJAX) attraverso i cosiddetti mock. QUnit non li supporta nativamente, ma si può aggiungere la funzionalità includendo qMock, un progetto in fase di sviluppo ma già abbastanza stabile.
Se invece abbiamo necessità di testare applicazioni AJAX, ci possiamo rivolgere a Mockjax, un plugin jQuery in grado di simulare in modo trasparente chiamate asincrone con tanto di tempi di risposta, nonché di caricare file statici come risposta:
//creo un nuovo mock
$.mockjax({
//l'url della richiesta
url: '/ajax/mia-azione',
//un file statico con dati di prova
proxy: '/mocks/dati-di-test.json',
//aspetta 750 millisecondi prima di
//restituire i dati
responseTime: 750
});
//il mock si attiva richiamando l'url '/ajax/mia-azione'
$.getJSON('/ajax/mia-azione', function (data) {
// ... dati di prova
});
Il secondo livello di simulazione riguarda l'interazione utente, ed in questo caso lo strumento più conosciuto è senza dubbio Selenium IDE.
Il software è installabile come plugin Firefox e, sebbene sia possibile redigere i passi dell'interazione in vari linguaggi (come Java e Ruby), la funzione più comoda e quella che registra automaticamente ogni vostra azione all'interno della finestra del browser per poi convertire il tutto in script esportabili.
Se invece preferite soluzioni JavaScript, una libreria estremamente funzionale ed intuitiva è FuncUnit (sviluppata da Jupiter e basata a sua volta su QUnit).
Per eseguire una simulazione, FuncUnit si appoggia a vere pagine web, aprendole nel browser ed interagendo con loro.
Ecco un semplice esempio di come sia possibile testare l'input di un utente in un campo form:
module("Testo un form",{
setup: function() {
//apro la pagina del form
S.open('mioForm.html')
}
});
test("simulare un utente che scrive", function () {
S('#mioInput').type('test', function () {
ok(S('#mioInput').val(), 'test', "funziona!");
});
});
In questa demo potete testare direttamente lo script (dovrete disabilitare il blocco popup)
Test Automation / Distributed Testing
Fin qui abbiamo visto come testare i nostri script e la loro interazione con l'input dell'utente. Tuttavia, per essere sicuri che l'applicazione sia stabile, dovremmo lanciare le nostre test suite sui browser più diffusi, tenendo conto delle differenze fra le varie release e le differenti performance legate al sistema operativo dell'utente.
La soluzione più elegante al problema è quella di utilizzare un software di test automation. Questi programmi vengono solitamente lanciati da riga di comando e si interfacciano con i browser attraverso un sistema client/server. Al momento i più importanti sono Selenium Remote Control (che permette di lanciare gli script esportati da Selenium IDE) e JsTestDriver.
In alternativa, Yahoo! ha da poco rilasciato in via sperimentale Yeti, un programma interamente scritto in JavaScript che gira sul server NodeJS e si integra nativamente con YUI.
Sebbene la configurazione iniziale di questi software possa risultare un po' ostica, i vantaggi in termini di tempo derivanti dal loro utilizzo ne hanno fatto strumenti essenziali soprattutto per i framework e le applicazioni JS di una certa complessità.
Più test, meno preoccupazioni
Con questa breve panoramica abbiamo visto i principali strumenti di testing delle applicazioni JavaScript, che possono evidenziare con facilità potenziali inconsistenze nel codice e bug spesso legati a differenze fra i motori JS dei browser, garantendo una maggiore stabilità e solidità del software.