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.

 

Implementazione delle fake class

Il metodo EseguiAnalisi per poter fuzionare, e testare, abbisogna di due oggetti, da passargli forzatamente nel costruttore: uno delegato all’invio delle mail e l’altro per l’accesso alla base dati. Queste classi devono implementare le interfacce assegnate: il trucco è questa classi, pur implementando correttamente dette interfacce, facciano tutt’altro di quello che ci si aspetta.

Per capire cosa voglio dire date un'occhiata al codice che Vi presento nel seguito,

Come si può vedere le classi InvioMailFake e RepositoryGenericoFake sono finte: promettono di inviare mail o interagire con base dati, ma in realtà non fanno assolutamente nulla di tutto questo.

Sono infatti classi menzognere che, pur implementando le corrette interfacce, hanno come unico scopo quello di far funzionare il metodo oggetto dello unit test.

E’ possibile chiamare questo tipo di classe come fake, mock o anche dummy (visto che il termine classe-menzognera sarebbe un po' lungo).

Nella realtà i più pedanti possono affermare che i termini sopra denotano cosa leggermente diverse tra loro, ma il nome di questo blog evidenzia come qui nessuno sia troppo interessato a queste sottili differenze.

Nel caso sopra potete vedere che la classe InvioMailFake è corredata di un contatore che indica quante volte viene richiamato il metodo SendMail. La cosa è lecita: una volta che l'implementazione dell'interfaccia è rispettata è possibile aggiungere quasiasi proprietà metodo o quant'altro ci possa servire.

Gli assert nello unit test sono due: uno che verifica appunto l'invio della mail (il contatore aggiunto ci permette di essere sicuri che il metodo in test abbia richiamato SendMail) e l'altro che verifica se il flag sul'anagrafica sia stato realmente posto a false.

La progettazione di classi fake è un’arte, e può anche essere divertente, ma indubbiamente è un’attività che è in grado di fare passare ogni voglia di test a chiunque.

Per esempificare la vita è stato creato un framework apposito: Moq. Questo semplifica, e non poco, l'attività degli scrittori di unit test.

Ovviamente questo framework non è l’unico, ma è indubbiamente uno dei più utilizzati. E proprio per questo ne parleremo nella prossima puntata.

.....e il virtual ? Perchè non ne hai parlato ?

Per completezza devo segnalare che, oltre alle modalità esposte che usano le interfacce, esiste anche un'altro possibile metodo per raggiungere le stesse funzionalità, e che fa uso del modificatore virtual.

 Qui l'aspetto più evidente che non implementa più alcuna interfaccia, e i metodi notevoli sono marcati come virtual. Ecco, quindi, una possibile implementazione della classe che contiene i metodi in test.

In questo caso il metodo non usa più le interfacce, ma direttamente l'istanza della classe. La cosa può risultare bizzarra, perchè il tutto può sembrare intestabile. Invece l'ereditarietà e il marcare i metodi come virtual ci vengono in aiuto.

Qui il trucco è che che i metodi notevolti sono virtual, per cui quando, per esempio, si richiama il metodo sendmail, viene richiamato il metodo della classe derivata (InvioMailFake), e non quello della classe base (InvioMail): è proprio per questo che è stata creata questa funzionalità.

Difficile dare delle indicazioni sicure su quale metodologia sia meglio adottare: interfaccia o virtual.

E' possibile affermare che addobbare dei metodi come virtual, senza poi sfruttare appieno l'override almeno nell'utilizzo del codice in produzione, secondo me non è una cosa molto elegante. Ma è un mio gusto personale.

Quindi in definitiva la risposta più sensata, ma anche la più odiosa perchè una non-ripsosta, risulta essere: dipende.

Io per dovere Ve l'ho riportata: fate un pò poi come volete...... Nel prossimo questa soluzione non sarà più utilizzata: però era interessante parlarne.

Linkografia

Informatica pressapochista - Un viaggio negli unit-test: Introduzione - Prima parte
Informatica pressapochista - Un viaggio negli unit-test: xUnit e dintorni – Seconda parte
Informatica pressapochista - Un viaggio negli unit-test: Fake class e dintorni - Terza parte
Informatica pressapochista - Un viaggio negli unit-test: Repository pattern - Quarta parte
Git Hub Page InformaticaPressapochista
Understand Stub, Mock and Fake in Unit Testing