Sottoscrivi il feed di DotNetToscana Seguici su Twitter Unisciti a DotNetToscana su Facebook Seguici su Linkedin

Testing Coogle: Introduzione

Rate this Content 1 Votes

Autore: Matteo Baglini

Sabato 24 Settembre si è svolto l'evento Unit Test Lab organizzato dalla nostra Community. Il laboratorio era suddiviso in sessioni teoriche e pratiche, queste ultime accompagnate da una serie di esercizi. Mentre i primi erano creati ad-hoc per concretizzare un singolo concetto appena spiegato dal punto di vista teorico, l'ultimo esercizio era una sorta di "campo di battaglia", una semplice ma completa applicazione di nome Coogle.

I principi che mi hanno guidato nello sviluppo di Coogle sono: pochi requisiti, dominio applicativo semplice, tante dipendenze infrastrutturali e nessun test. L'obiettivo finale di questo esercizio era scrivere i test applicando quanto visto durante la prima parte dell'evento. La mia volontà era quella di ricreare, in piccola scala, la situazione che i partecipanti avrebbero trovato a lavoro, questo perché un'applicazione scritta senza seguire il principio di Design for Testability è un'applicazione sicuramente piena di dipendenze rigide, sia fra i moduli interni al contesto applicativo sia verso attori esterni come il file system. Da qui l'idea del "campo di battaglia".

L'esercizio è piaciuto più del previsto, tant'è che i partecipanti erano tutti "immersi" nel codice e nessuno mi ascoltava più :-) A fine giornata diversi di loro mi hanno chiesto di scrivere un articolo che illustrasse la mia soluzione. Sinceramente credo che mostrare solo la soluzione finale sia sbagliato, quello che conta non è la meta ma il viaggio che ci accompagna verso di essa. Quindi ho deciso di scrivere una serie di articoli, chiamiamoli step-by-step, che espongono come affrontare, analizzare, modificare e testare le varie parti dell'applicazione. In questo primo articolo introduttivo mostrerò l'applicazione e quali erano le richieste del committente (naturalmente immaginario).

Cosa non è

Mostrare un corretto processo di sviluppo software non faceva parte dell'obiettivo del laboratorio, quindi i requisiti possono sembrarvi incompleti o fumosi. Meglio! :-) Vedremo come il test, ma soprattutto l'analisi dello scenario da testare, ci guiderà alla scoperta di questi punti oscuri. Inoltre, non è stata applicata nessuna tecnica di Object-Oriented Design, come per esempio il Test-Driven Development, piuttosto il codice è volutamente "scadente" e assemblato in maniera "brutale", prendendo spunto da applicazioni Legacy con le quali ho a che fare quotidianamente, in modo tale da rendere l'esercizio il più reale possibile.

Codice

Il codice lo trovate nel nostro repository su Codeplex. Gli urls per collegarsi al repository sono indicati nella pagina Source Code. Maggiori informazioni potete trovarle visitando la pagina Source control client connection instructions di Codeplex.
Per seguire meglio l'evoluzione del codice ogni articolo sarà abbinato ad uno specifico Change Set. Il primo, quello relativo a questo articolo, è il 70460. Infine, per coloro che erano presenti all'evento, sappiate che il codice è stato leggermente modificato senza però aver cambiato il proprio comportamento.

Requisiti e caso d'uso

Il committente, come sempre, ci ha fornito delle specifiche semplificate e poco chiare, vediamole: "Serve un'applicazione desktop per ricercare nel nostro archivio spedizioni i dati relativi ad un container".
Noi da bravi analisti abbiamo approfondito la questione intervistando il committente ed in fine abbiamo stilato il seguente caso d'uso.

Scenario base: Il sistema visualizza la finestra principale. L'utente inserisce un codice contenitore e clicca sul pulsante ricerca oppure preme Invio. Il sistema ricerca i dati anagrafici del container ed i dati delle relative spedizioni, in fine visualizza i risultati delle ricerche nella finestra principale
Sceanario alternativo 1: L'utente avvia una ricerca senza inserire un codice contenitore. Il sistema non deve eseguire nessuna ricerca.
Sceanario alternativo 2: L'utente ricerca un contenitore senza spedizioni. Il sistema deve rispondere con un messaggio esplicativo e visualizzare solo il risultato della ricerca per i dati anagrafici.
Sceanario alternativo 3: L'utente ricerca un contenitore non presente in archivo. Il sistema deve rispondere con un messaggio esplicativo.
Note: Il codice container, anche se inserito manualmente dall'utente, potrebbe essere stato copiato da un altro sistema e quindi avere formati differenti contenenti vari separatori.

L' applicazione

Il layer di presentazione è stato sviluppato in WPF, la logica applicativa è tutta nel code-behind. Viene sfruttata una classe Singleton per accedere al database ed eseguire le query. Dato che l'applicazione accede ai dati in sola lettura essi sono caricati in oggetti immutabili. Per i messaggi esplicativi degli scenari alternativi è stato utilizzato il motore di text-to-speech presente in Windows e facilmente accessibile da .NET. Prima di eseguire l'applicazione seguite le istruzioni descritte nel file Readme.txt presente nella Solution. La finestra principale all'avvio si presenterà così:

Coogle finestra principale

Inserendo un codice valido presente in archivio (nel file Note.txt trovate una serie di codici container utili) e cliccando ricerca il risultato sarà all'incirca il seguente:

Coogle finestra principale con risultato ricerca

Il progetto di test (Coogle.Test) referenzia già tutti gli assemblies necessari: Coogle, NUnit e Rhino.Mocks.

 

Scelta dei test e ordine degli articoli

In un mondo ideale, con tempo infinito, potremmo porci l'obbiettivo finale di otterene la più alta percentuale di code coverage possibile. A quel punto l'ordine dei test è quasi soggettivo, tanto alla fine li dobbiamo fare tutti. Per esempio uno sviluppatore potrebbe partire da testare le classi più semplici per guadagnare confidenza con la code-base piuttosto che partire da testare le query verso il database. Nella realtà la scelta dei test è un aspetto molto importante da non trascurare e il tempo non è infinito, quindi dobbiamo spenderlo nel testare prima le parti che portano valore, sia per al committente (scenario ben testato, solido) che per noi sviluppatori (miglior qualità interna) e poi tutto il resto. Questi aspetti sono fortemente influenzati dal processo di sviluppo software adottato, in questo caso come già puntualizzato non ne abbiamo utilizzato uno, quindi ci lasceremo guidare dll'unica fonte valida ed allo stesso tempo disponibile, il caso d'uso. Mi spiego meglio. Dato che un caso d'uso è scritto come un elenco di interazioni di tipo evento\risposta tra l'utente ed il sistema, possiamo prendere in esame la prima interazione ed inziare a testare da li. Analizzando il caso d'uso gli scenari (indicati di seguito a grandi linee) da testare sono:

  1. validazione dell'input.
  2. query verso il database.
  3. visualizzazione dei risultati delle ricerche.
  4. messaggio esplicativo per lo Sceanario alternativo 3.
  5. messaggio esplicativo per lo Sceanario alternativo 2.

Perchè non invertire l'ordine di 1 e 2?
Semplice, per una questione di dipendenza. Le query utilizzano il codice container inserito dall'utente per ricercare i dati, se il primo non è corretto figuriamoci il risultato delle query. Lo stesso ragionamento vale per gli altri scenari. L'ordine di 4) e 5) è un po' discutibile, nel senso che potremmo farli in un colpo solo, oppure invertirli. Personalmente ho scelto questo ordine perchè, ipotizzando per un attimo che non esista il messaggio esplicativo, nel caso 5) l'utente riceve un feedback dal sistema, ovvero la visualizzazione dei dati anagrafici del container, mentre nel secondo no, quindi voglio assicurarmi prima il funzionamento dello Sceanario alternativo 3 e poi il 2.
L'ordine degli articoli riflettà l'ordine di scelta ed importanza del test. Alla fine della serie ci saranno parti di codice non coperti da test perchè ritenuti di basso valore.

Conclusione

Per finire un consiglio, prima di vedere la mia soluzione cimentatevi nella realizzazione della vostra versione. In questo modo favorirete l'apprendimento tramite il confronto.