Testing delle date in JavaScript

Da qualche tempo lavoro per Taskomat, un software ERP che ha come target il mondo dei freelance digitali. Per migliorare l’esperienza d’uso abbiamo deciso di implementare una cache a tempo per le chiamate a servizi REST più impegnative. Se una chiamata viene ripetuta entro un certo intervallo di tempo con gli stessi parametri di ricerca, questa non viene effettuata, ma viene ritornato l’ultimo valore disponibile. All’interno di questo meccanismo di caching esiste una funzione che all’incirca questa forma:

Come vedete questa funzione fa uso di Date.now per farsi restituire i millisecondi passati a partire dal 1 Gennaio 1970 fino al momento attuale. Usa poi il metodo getTime della data passata in ingresso, sempre per ottenerne la rappresentazione in millisecondi trascorsi dal 1 Gennaio 1970.

Come testereste questa funzione?

In pratica il problema nel testare questa funzione sta tutta nella dipendenza da Date.now. In fase di test il valore di Date.now cambierà, ogni volta che il test verrà lanciato. Questa meccanismo rompe il principip F.I.R.S.T. dello unit testing, in particolare quello della ripetibilità. In questo post vedremo alcune tecniche che ci permettono di superare questo ostacolo in maniera semplice. I test che vedremo girano in Jest, test runner sviluppato in casa Facebook. Ma come potete immaginare sono facilmente portabili in altri ambienti di test.

Override

In questo primo esempio sfruttiamo il fatto che JavaScript non è un linguaggio tipato. Possiamo quindi modificare l’oggetto globale Date, facendo in modo che ci vengano restituiti dei valori fissi, sui quali possiamo scrivere facilmente le nostre asserzioni.

In questo esempio Date.now restituisce sempre la data del 1 gennaio 1970 alle 00:00:00 e getTime sempre il 1 gennaio 1970 alle 00:00:05. In questo modo l’intervallo è fisso a 5 secondi. Per modificare l’oggetto Date ci appoggiamo alle funzioni beforeAll e afterAll. Queste funzioni vengono invocate, come si può intuire dal nome, all’inizio e alla fine della nostra suite di test. Notate che alla fine della suite ripristiniamo il comportamento “naturale” dell’oggetto Date. Questo meccanismo è fondamentale per rispettare il principio F.I.R.S.T., altrimenti i test successivi a questi sarebbero dipendenti dalle modifiche a Date fatte in precedenza.

timekeeper

Se avete molte funzionalità che dipendono dall’oggetto Date, avrete una buona quantità di test dove avete bisogno di applicare questa tecnica. In questi casi potrebbe convenire utilizzare timekeeper, piccola libreria che vi permette di modificare semplicemente il comportamento delle date senza fare delle modifiche esplicite su un oggetto di sistema.

Utilizzando freeze forziamo la data corrente sempre all’instante 0, cioè il 1 gennaio 1970 alle 00:00:00, esattamente come nell’esempio precedente. Una volta completati i test, utilizziamo il metodo reset per ritornare alla normalità.

Dependency Injection

Proviamo ora con una soluzione più “accademica”. Come probabilmente sapete EcmaScript6 ha aggiunto la gestione dei moduli in maniera nativa, senza l’aggiunta di librerie esterne come RequireJS. Questo permette di poter implementare facilmente il pattern della dependency injection. Per il prossimo esempio modifichiamo la nostra funzione in modo da accettare Date.now come dipendenza.

In pratica, ora abbiamo una factory che genera la nostra funzione. Questa factory prende in ingresso il parametro now che viene poi utilizzato all’interno della funzione restituita per il calcolo dell’intervallo di tempo. In questo modo all’interno dei test possiamo semplicemente passare un mock alla nostra factory, ottenendo una nuova funzione che genera valori prestabiliti.

Anche in questo caso forzando i valori a 0 e 5000, otteniamo sempre l’intervallo di 5 secondi che ci serve per testare il nostro codice in maniera semplice.

Notate infine che l’export di default della nostra funzione è l’invocazione della factory con Date.now passato come parametro. In questo modo il resto dell’applicazione importa sempre la versione realmente funzionante, senza dover tutte le volte invocare la factory.

Moment.js


Se avete funzioni in cui dovete fare manipolazioni complesse sulle date, probabilmente avrete importato nel vostro progetto una libreria di utility che vi permette di non reinventare la ruota.
La più utilizzata nel mondo JavaScript è sicuramente Moment.js. La nostra funziona riscritta in modo da utilizzare Moment.js ha questa forma.

Moment.js mette a disposizione il suo prototype in maniera strutturata, proprio per poter testare al meglio il codice.

Vedete come applichiamo lo stesso meccanismo di override visto in precedenza, ma lavoriamo direttamente sulla proprietà fn di Moment.js che non è altro che un wrapper attorno al suo prototype. In questo caso poi ci basta fare il mocking del metodo diff per forzare il solito intervallo di 5 secondi e testare agevolmente il tutto.

Conclusioni

Come avete visto il problema del testing delle date può essere risolto in molti modi differenti. La strada della dependency injection è sicuramente la più elegante e “sicura”, ma è percorribile in maniera semplice solo in un ambiente EcmaScript6. In ogni caso però la strada dell’override è quella che nella community JavaScript ha da sempre avuto più proseliti. Difatti Moment.js, uno standard de facto per quanto riguarda la manipolazione delle date in JavaScript, utilizza proprio questa tecnica.

Se volete dare un’occhiata al codice degli esempi e giocare con le varie tipologie di testing, trovate tutto in questo repository GitHub.

La canzone di questo post è Out Of Time dei Blur dal loro album Think Tank. Buon ascolto.

Flattr this!

Pubblicato in Javascript Taggato con: ,

Lascia un commento

Il tuo indirizzo email non sarà pubblicato.