Ho appena letto questo post segnalato da Lorenzo che
mi ha fatto riflettere. Le modalità del database unit testing descritto
assomigliano moltissimo alle modalità con cui
sfrutto nUnit e Test Driven
.NET nelle mie ultime settimane. Mi spiego meglio, ma prima ho
bisogno di fare una piccola premessa.
Sabato mattina mio fratello ha messo live la nostra piccola
applicazione per la gestione di concessionari d'auto, creata ad-hoc per un
concessionario piuttosto imponente di Lodi. Lo sviluppo di questa applicazione
ha richiesto molto più tempo di quello preventivato inizialmente, per tutta una
serie di motivi, di cui magari parlerò in un altro post (anche un fallimento può
farci guadagnare experience point e farci salire di livello). Nelle
fasi iniziali di sviluppo io e mio fratello abbiamo prima creato un domain model
strutturato in grado di rappresentare adeguatamente gli oggetti del dominio
applicativo. Senza scendere troppo nel dettaglio, ci sono tutta una serie di
oggetti che vanno dall'atto di vendita tipico, o a quello specifico nel caso di
nuovo veicolo o veicolo usato, c'è un'anagrafica clienti categorizzata, importi
dettagliati. Alla fine, ci siamo ritrovati a gestire qualcosa come ~15 classi
gerarchicamente legate fra di loro, tutte mappate con NHibernate per essere
persistite su database. E qui arriva il mio discorso legate ad
nUnit.
Ho creato un progetto dedicato allo unit testing. Attraverso il codice,
istanzio oggetti del mio domain model, dal più semplice al più complesso. Poi li
salvo su database con NHibernate. Ho creato ~40 metodi di test che salvano e
rileggono gli oggetti del domain model, assicurandomi ogni volta che l'oggetto
venga persistito correttamente. Questo mi assicura che il database sia sempre
congruo rispetto al domain model implementato. Questo mi assicura che il file di
mapping di NHibernate sia sempre corretto. Questo mi assicura che il mio codice
continui a funzionare anche se rinomino un campo, o una tabella, o una
stored-procedure. Riporto qui sotto a titolo di esempio uno dei tanti metodi che
ho scritto per testare la persistenza.
[Test]
public void CreaNuovo15()
{
AttoVendita av = new AttoVendita();
av.Denominazione = "CreaNuovo15";
av.Finanziaria = "Finanziaria";
av.Locatario = "Locatario";
av.Modello = "Modello";
av.NumeroFattura = "NumFatt";
av.Protocollo = "Protocollo";
av.ProtocolloVendita = "Protocollo Vendita";
av.Marca = DataProvider<Marca>.LoadObject(2);
av.Nuovo = null;
// Aggiungo i dettagli importo all'AttoVendita
DettaglioImporto di = new DettaglioImportoAttoVendita(av);
di.Esecutore = DataProvider<Soggetto>.LoadObject(5);
di.Lavorazione = DataProvider<Lavorazione>.LoadObject(1);
di.ImportoUnitario = 80;
di.Quantita = 1;
av.Importi.Add(di);
// Aggiungo un oggetto Usato a questo AttoVendita
Usato us = new Usato(av);
us.AnnoImmatricolazione = 2000;
us.ArchiviazioneUsato = "arch.usato";
us.ClasseVeicolo = ClasseVeicoloEnum.Rimorchio;
us.DataFatturaExProprietario = DateTime.Today;
us.Denominazione = "denominazione";
us.ExProprietario = DataProvider<Soggetto>.LoadObject(7);
us.Notaio = DataProvider<Soggetto>.LoadObject(6);
us.NumeroStock = "numero stock";
us.PassaggioProprietà = true;
us.Repertorio = "repertorio";
// Aggiungo un dettaglio importo all'oggetto Usato
di = new DettaglioImportoUsato(us);
di.Esecutore = DataProvider<Soggetto>.LoadObject(5);
di.ImportoUnitario = 45;
di.Lavorazione = DataProvider<Lavorazione>.LoadObject(1);
di.Quantita = 3;
us.Importi.Add(di);
// Aggiungo un oggetto VenditaUsato all'oggetto Usato
VenditaUsato vendita = new VenditaUsato(us);
vendita.DataFattura = DateTime.Today.AddDays(1);
vendita.NumeroFattura = "num.fattura";
vendita.PartitaIva = "p.iva";
vendita.Provincia = "MI";
// Aggiungo un dettaglio importo all'oggetto VenditaUsato
DettaglioImportoVenditaUsato divu = new DettaglioImportoVenditaUsato(vendita);
divu.Esecutore = DataProvider<Soggetto>.LoadObject(5);
divu.ImportoUnitario = 11;
divu.Lavorazione = DataProvider<Lavorazione>.LoadObject(1);
divu.Quantita = 11;
vendita.Importi.Add(divu);
us.Vendita = vendita;
av.Usato = us;
DataProvider<AttoVendita>.SaveObject(av);
newID = av.ID;
Debug.WriteLine("Inserito ID = " + newID.ToString());
Assert.AreNotEqual(newID, 0, "L'entità non è stata salvata!");
}
Mio fratello, che inizialmente era scettico, alla fine
ha afferrato l'effettivo vantaggio, anche perchè all'aumentare dei metodi
di test, il tutto diventa sempre più efficace. Non è una cosa da poco,
anche perchè il mio domain model attualmente ha un solo oggetto
root chiamato AttoVendita, al di sotto del
quale vivono numerosi altri dettagli che non sempre esistono, a
seconda dei casi. Quindi è molto importante verificare sempre che la gerarchia
di business object venga mappata sul database nelle tabelle corrette, con
relazioni 1:1 o 1:molti. Usare Test Driven .NET, cliccare
col pulsante destro sul progetto dedicato, cliccare Run Tests e
vedere la scritta ~40 test passed è motivo di
soddisfazione, credetemi.
Beh, insomma, quel post che ho segnalato all'inizio sembra molto fare il mio
stesso ragionamento. Il database unit testing è quanto di più vicino abbia
trovato rispetto al mio modo di intendere lo unit testing. Indipendentemente
dallo strumento tecnologico utilizzato, sia chiaro. Se tutti i miei test
passano, sono sicuro al 100% che cliccando il pulsante Salva
della mia interfaccia utente, l'oggetto verrà salvato correttamente, senza
troppe storie, dal momento che la UI del software richiama esattamente gli
stessi metodi del data provider testati durante lo unit testing. E ci
mancherebbe altro, altrimenti cosa servirebbe farli?