Test Against Database

La scorsa settimana durante l'incontro di UgiAlt.net abbiamo parlato di unit test e discutendo diquesto tema si finisce sempre per andare a toccare uno dei temi più insidiosi e che spesso allontanano gli sviluppatori dalla scrittura dei test: i test di integrazione con il database.

Chiunque abbia provato a scrivere test si è scontrato con l'implementazione di un test che deve andare a scrivere su una tabella di un database che ha un numero elevato di foreign key. La scrittura di questi test ha in genere un notevole overhead per portare il db in uno stato consinstente prima di poter eseguire il vero test.

Quali approcci possiamo usare?

Gli approcci che ho provato e ho trovato più efficaci sono due.

Il primo consiste nel avere un database di test in uno stato noto e usare la convenzione che ci sono già alcuni dati noti che possiamo usare nei test. Cosi se ad esempio dobbiamo inserire un indirizzo possiamo basarci sul fatto che la tabella delle provincie contiene già il record relativo alla città di Brescia e che il test conosce l'id di questo record:

 

[Test] public void SaveAddress_CreateANewValidAddressAndSaveItOnDatabase_ShouldSucced() { AddressService as = IoC.Resolve<AddressService>(); Address a = new Address(); a.Street = "via Mantova 6"; a.City = GetBrescia(); as.Save(a); // ...Assert e verifca } private City GetBrescia() { return new City(Id=5, Name="Brescia"); }

L'ipotesi è molto forte e può sembrare troppo vincolante e difficile da rispettare, soprtattutto quando il database è grosso e cambia spesso durante lo sviluppo. Per questi motivi talvolta è meglio fare in modo che il database venga ricostruito ad ogni avvio di sessione di test utilizzando una serie di script TSQL che costruiscono le tabelle e inseriscono i dati per i test. Quando l'applicazione ha raggiunto una certa maturità si può utilizzare anche un backup da restorare ad ogni sessione.

 

Un secondo approccio consiste nel fare in modo che ogni test si occupi di portare il database in uno stato consistente per se stesso. Sempre in riferimento all'esempio di prima il test dovrebbe inserire la città di Brescia nella tabella delle città ottenendo ogni volta un nuovo Id (fornito dal DB) da utilizzare nella costruzione dell'oggetto City per il test sull'indirizzo.

La differenza con l'approccio precedente è che in questo secondo caso i test sono indipendenti dai dati presenti nel database e ogni test (o ogni fixture) è responsabile dei suoi dati che dovrà inserire durante il setup e rimuovere durante il teardown.

Questo approccio è più oneroso in termini di codice da scrivere ma è migliore dal punto di vista della pulizia. Tramite opportuni metodi di helper è facile scrivere le query necessarie all'inserimento dei record necessari ai test.

In entrambi i casi è importante che i test siano "educati" e rimuovano tutti gli eventuali record inseriti per evitare che altri test vengano influenzati dalla presenza di dati sporchi nel database.

Print | posted on domenica 2 marzo 2008 20.35

Comments on this post

# re: Test Against Database

Requesting Gravatar...
Ciao,
non è meglio un MDF da attaccare/staccare al volo per eseguire il test rispetto ad un restore? Soprattutto se si usa un SQL Server Express eseguito nel contesto dell'utente del test.

Un'alternativa potrebbe anche essere il Data Generator della DB Pro, che ha il vantaggio di generare dati casuali ma deterministici.
Left by Lorenzo Barbieri on mar 02, 2008 8.43

# re: Test Against Database

Requesting Gravatar...
Si, l'attach/detach in certi casi è meglio perchè più performante a patto di eseguire sempre una copia del file MDF originale per non "sporcarlo".
Il restore è più comodo perchè non devi occuparti della cancellazione e/o detach alla fine dei test.

La Db pro non l'ho provata, se genera dati casuali potrebbe essere comoda in alcuni contesti, ma spesso è più importante avere dati "noti".
Left by Emanuele DelBono on mar 02, 2008 9.05

# re: Test Against Database

Requesting Gravatar...
Con il Data Generator puoi anche "pescare" dati da un DB di riferimento, e ricostruire dati "casuali" in maniera deterministica (ovvero, random ma sempre con la stessa sequenza).
Ti consiglio di provarla, magari anche guardando il modulo di unit test del codice TSQL nativo, e di dare un'occhiata agli articoli miei (uno con anche la descrizione delle novità degli ultimi mesi) e di Davide Mauri sul Developer Center del ciclo di vita del software di MSDN Italia.
Left by Lorenzo Barbieri on mar 02, 2008 9.08

Your comment:

 (will show your gravatar)
 
Please add 5 and 7 and type the answer here: