Nelle scorse puntate ho parlato solo unit test che riguardano genericamente delle classi: ma quando si scrive software dotato di UI che valenza possono avere questi tipi di test ?

Ovviamente se il software è ben strutturato esisteranno delle classi di servizio atte a implementare logiche di funzionamento e che saranno usate dalla logica di funzionamento della UI: in tale ambito gli unit test possono essere utilizzati per controllarne la logica di funzionamento.

Ma se le UI sono scritte seguendo il pattern MVVM ecco che è possibile è possibile anche fare anche di più.

Siccome la logica di funzionamento di una maschera è proposta dalla relativa classe che rappresenta la ViewModel, ecco che in pratica implementare degli unit test per questa nella pratica corrisponde ad eseguire dei test sul comportamento della UI stessa.

Dirò una cosa che a qualcuno potrebbe fare storcere il naso: io penso che la ragione principale per adottare il pattern MVVM per UI sia proprio quello di poter sottoporre i relativi ViewModel a unit test, in modo tale da eseguire in modo indiretto la verifica del corretto funzionamento che avranno le relative UI.

E’ importante però sottolineare che se si vuole scrivere con successo degli unit-test per un ViewModel occorre essere certi che questa sia stata implementata seguendo le “regole sacre” del pattern MVVM.

In particolare le seguenti.

> La classe non deve dipendere in alcun modo da elementi o istruzioni che coinvolgono elementi di UI (creazione di pagine, modifiche di stili, consumare eventi proposti dalla UI, visualizzazione di messaggi di warning/errrore/avviso)

> Non deve usare alcun metodo statico offerto da altre classi

> Quando occorre utilizzare usare servizi esterni occorre tassativamente usare i componenti coinvolti via interfaccia (cioè usare solo l’interfaccia, e non l’oggetto), che quindi saranno risolti in concrete object utilizzando il Dependency Injection (questa parte non dovrebbe avere più alcun segreto perchè espliticitata nelle scrose puntate della serie).

Usando i corretti pattern di progettazione di una classe, e utilizzando le opportune fake class, è sempre possibile fare tutti gli unit test del caso.

Come visto le classi menzognere (l'altro nome che abbiamo dato alle fake class) non fanno altro che implementare delle interfacce, in modo da permettere al codice in test di interagire correttamente con le funzionalità esterne, simulandole.

Tutto meraviglioso però è innegabile che scrivere fake class è una rottura di scatole infinita, e direi che dopo un po' ti toglie la voglia di implementare qualsiasi unit test.

Mi permetto di sottolineare il punto: non è sbagliato implementare unit test con l'ausili di fake class, semplicemente questa è un’attività generalmente è molto onerosa.

Per ovviare a questo sforzo ci viene in aiuto un framework che è in grado di aiutarci a bestia nella creazione di classi fake: questo meraviglioso alleato si chiama Moq.

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.