Nella scorsa puntata ho eseguito un po' di refactoring alla classe in analisi, che ora può essere anche mostrata al mondo intero senza grossa vergogna.

Le attività eseguite sono state volte a trasformare la classe in modo tale che questa segua il principio DIP (Dependency inversion principle), l'aaplicazione del qualce ci ha permesso di trasformare le strong dependency (dipendenza dai servizi di invio mail e accesso ai dati) in loosely coupled dependency.  Giusto per dare due nomi e fare vedere che qui il pressapochismo ci piace, ma le cose supergiù le sappiamo.

Il refactoring mostrato ci ha permesso di ottenere dei grossi vantaggi in termini di manutenibilità del codice nonché possibilità di estensione delle funzionalità.

Va da sé che quando si progetta un refactoring del codice con le modalità qui mostrate occorre sempre avere bene in mente il costo di implementazione rispetto ai vantaggi ottenuti.

E’ possibile osservare come per progetti che implementano algoritmi semplici e che difficilmente subiranno modifiche ed estensioni nel tempo, ma sopratutto per i quali non si prevede l’utilizzo di unit test, non sia efficace mettere in mezzo un refactoring come quello esposto in queste pagine, poiché il trade-off costi/benefici è sicuramente svantaggioso.

Attenzione: qui si parla di refactoring, cioè modifica di codice già funzionante. In fase di creazione di un nuovo progetto, invece, ritengo non ci siano molte scuse.

Quando si crea da zero una nuova classe penso sia sempre utile utilizzare i principi e i pattern di buona programmazione come quelli qui evidenziati, ma non solo.

Giunti a questo punto è possibile mettere a frutto il refactoring mostrato e implementare gli unit test. Per inciso: con la versione iniziale della classe questo NON era possibile, o almeno era molto grandemente difficile.

Solo ora, dopo le modifiche introdotte, vedrete che l’attività di equipaggiare la classe con unit test diventa un’attività semplice e intuitiva.

Nella scorso post di questa serie ho esposto un possibile refactoring della classe di esempio, al fine di renderla compatibile con l'applicazione degli unit test.

Qui continuerò la stessa attività, al fine di risolvere la dipendenza ancora rimasta. Infatti è facile verificare che permane nella classe la logica che si occupa dell'accesso alla base dati e questo, come detto, no buono !

Più esplicitamente il problema è che nella classe viene usato esplicitamente la classe LocalDbContext, che è il context di entity framework per interagire con il database

Introduzione del repository pattern

Per risolvere questa dipendenza useremo un approccio molto simile a quello visto per sistemare l'invio email: solo che in questo caso la cosa diventa leggermente più complessa. Inoltre il problema in analisi (cioè le dipendenze derivanti dalla necessità di accedere a una base dati) è talmente generalizzato che esiste, per risolverlo, un pattern ben definito, chiamato repository pattern.

Questo è stato creato creare un ulteriore livello di astrazione all'accesso ai dati. In pratica implementando il pattern si separa la logica di accesso alla base dati alle logiche di business: in soldoni la classe InvioMailNewsLetter (logica di business) nulla deve conoscere su come ricavare i dati, e quindi non deve in alcun modo avere alcun riferimento diretto a entity framework.

 Ecco quindi il codice modificato in modo tale da utilizzare il pattern repository.

Piccola storia.

La mattinata si presentava foriera di novità e cose nuove: la sera precedente avevo letto per la prima volta degli unit test, e incredibilmente avevo capito subito il meccanismo di funzionamento e le regole base per utilizzare lo strumento in modo efficace.

Ero intenzionato ad applicare quanto appreso nel team di cui facevo parte, così da rendere il software in sviluppo più testato e più stabile.

Con questi pensieri in testa avevo varcato il portone per andare in ufficio e, senza nemmeno prendere il caffè, mi ero precipitato alla scrivania per scrivere il mio primo unit test.

Avrei così fatto vedere al mondo intero e a miei colleghi come si usava lo strumento. Già sognavo l’approvazione del capo, nonché grandi apprezzamenti dei colleghi e magari, in mezzo a tutto quel clima di approvazione che avrei potuto creare, anche il numero di telefono di quello splendido pezzo di receptionista appena assunta (qualcosa tipo la foto a fianco) per poterla invitare fuori (erano anni in cui, per approcciare una ragazza, non si usavano i social....... ma si chiedeva il numero di cellulare).

Dopo aver iniziato a scrivere il primo test con la prima classe (tra l’altro la più semplice in fatto di algoritmo), mi rendo conto subito che le cose non sono così semplici come avevo immaginato.

Infatti la classe sotto attacco presentava svariate dipendenze con classi esterne, che rendevano semplicemente impossibile scrivere degli unit-test in modo lineare e semplice come invece mi ero immaginato.

Dopo una giornata di parolacce e preghiere (anche alternate consecutivamente) mi sono reso conto che gli unit-test vanno di pari passo con le classi fake, e/o un framework di mocking, come Moq (che, per inciso, all’epoca del racconto Moq ancora non esisteva, anzi forse il suo autore forse nemmeno era nato).

Questa necessità nella lettura serale non l’avevo compresa, sicuramente preso dall’entusiasmo.

Quanto sopra, con buona approssimazione, è la cronistoria di quando ho tentato la prima volta di usare gli unit-test, senza averne approfondito tutti gli aspetti.

Per terminare la storia la famosa settimana si è conclusa con me che riprendo ho ripreso in mano i “sacri testi” e studio con precisione anche le pagine dedicate alle fake-class.

Per la cronaca alla fine sono riuscito nell’intento, ma mi ci sono volute ancora diverse settimane di studio e refactoring del codice esistente e, manco a dirlo, la bella receptionista nel frattempo si era fidanzata con un mio collega, che non aveva perso tempo poiché la sera invece di studiare gli unit-test l’aveva invitata fuori.

La morale è semplice: se avete qualche bella ragazza (o ragazzo) da invitare fuori, state lontani dagli unit test.

Nello scorso post ho fatto un mare di chiacchere, ma non sono ancora arrivato al succo di come si creano praticamente gli unit-test.

Mi è sembrato corretto dare prima degli elementi di contesto per meglio comprenderne fini e utilizzi.

Poi tutto sommato a pensarci bene il blog è mio, per cui per quale motivo mi devo giustificare ??

Quindi dicevo: gli unit-test sono una metodologia che permette di programmare dei test in modo automatico.

L’idea è quella di isolare dal contesto gli elementi principali del software e programmare dei test continui per assicurarne il corretto funzionamento.

Tutto questo suona bene, ma… non è tutto oro quello che luccica. Infatti implementare gli unit-test è indubbiamente faticoso, e introduce anche la necessità di conoscere tutta una serie di strumenti che servono semplicemente a favorire i test, e a null’altro.

In questa piccola serie proporrò due di questi strumenti che ritengo indispensabili: xUnit e Moq.