Domanda

L'estate scorsa stavo sviluppando un'app CRUD ASP.NET/SQL Server di base e uno dei requisiti era lo unit testing.Ho riscontrato alcuni problemi quando ho provato a eseguire il test rispetto al database.Per quanto mi risulta, i test unitari dovrebbero essere:

  • apolide
  • indipendenti gli uni dagli altri
  • ripetibile con gli stessi risultati, ad es.nessun cambiamento persistente

Questi requisiti sembrano essere in contrasto tra loro durante lo sviluppo di un database.Ad esempio, non posso testare Insert() senza assicurarmi che le righe da inserire non siano ancora presenti, quindi devo prima chiamare Delete().Ma cosa succede se non ci sono già?Quindi dovrei prima chiamare la funzione Exists().

La mia soluzione finale prevedeva funzioni di configurazione molto grandi (che schifo!) e un caso di test vuoto che veniva eseguito per primo e indicava che la configurazione veniva eseguita senza problemi.Ciò significa sacrificare l’indipendenza dei test pur mantenendo l’apolidia.

Un'altra soluzione che ho trovato è racchiudere le chiamate di funzione in una transazione che può essere facilmente ripristinata, ad esempio XtUnit di Roy Osherove.Funziona, ma coinvolge un'altra libreria, un'altra dipendenza e sembra una soluzione un po' troppo pesante per il problema in questione.

Allora, cosa ha fatto la comunità SO di fronte a questa situazione?


tgmdbm ha detto:

In genere si utilizza il tuo framework di test unitari automatizzato preferito per eseguire test di integrazione, motivo per cui alcune persone si confondono, ma non seguono le stesse regole.È consentito coinvolgere l'implementazione concreta di molte delle tue classi (perché sono state testate con unità).Stai testando Come le tue classi concrete interagiscono tra loro e con il database.

Quindi, se ho letto bene, non c'è davvero modo di farlo effettivamente testare l'unità di un livello di accesso ai dati.Oppure, uno "unit test" di un livello di accesso ai dati comporterebbe il test, ad esempio, dei comandi SQL generati dalle classi, indipendentemente dall'effettiva interazione con il database?

È stato utile?

Soluzione

Non esiste un modo reale per testare un database se non quello di affermare che le tabelle esistono, contengono le colonne previste e hanno i vincoli appropriati.Ma di solito non vale davvero la pena farlo.

Di solito non lo fai unità testare il database.Di solito coinvolgi il database integrazione test.

In genere si utilizza il framework di test unitario automatizzato preferito per eseguire test di integrazione, motivo per cui alcune persone si confondono, ma non seguono le stesse regole.Puoi coinvolgere l'implementazione concreta di molte delle tue classi (perché sono state testate in unità).Stai testando il modo in cui le tue classi concrete interagiscono tra loro e con il database.

Altri suggerimenti

DBunità

È possibile utilizzare questo strumento per esportare lo stato di un database in un determinato momento e quindi, quando si eseguono test unitari, è possibile riportarlo automaticamente allo stato precedente all'inizio dei test.Lo usiamo abbastanza spesso dove lavoro.

La solita soluzione alle dipendenze esterne negli unit test è utilizzare oggetti fittizi, vale a dire librerie che imitano il comportamento di quelli reali rispetto ai quali si sta testando.Questo non è sempre semplice e talvolta richiede un po' di ingegnosità, ma esistono diverse buone librerie fittizie (freeware) per .Net se non vuoi "crearne una tua".Me ne vengono subito in mente due:

Il rinoceronte prende in giro è uno che ha una buona reputazione.

NMock è un altro.

Sono disponibili anche molte librerie finte commerciali.Parte della scrittura di buoni test unitari consiste effettivamente nel progettare il codice per essi, ad esempio utilizzando le interfacce dove ha senso, in modo da poter "deridere" un oggetto dipendente implementando una versione "falsa" della sua interfaccia che tuttavia si comporta in modo modo prevedibile, a scopo di test.

Nei mock di database, questo significa "prendere in giro" il proprio livello di accesso al DB con oggetti che restituiscono oggetti di tabella, riga o set di dati costituiti da gestire per i test unitari.

Dove lavoro, in genere creiamo le nostre librerie finte da zero, ma ciò non significa che devi farlo tu.

Sì, dovresti rifattorizzare il tuo codice per accedere a repository e servizi che accedono al database e puoi quindi simulare o stub quegli oggetti in modo che l'oggetto sotto test non tocchi mai il database.Questo è molto più veloce che memorizzare lo stato del database e reimpostarlo dopo ogni test!

Consiglio vivamente Moq come la tua cornice beffarda.Ho usato Rhino Mocks e NMock.Moq è stato semplicissimo e ha risolto tutti i problemi che avevo con gli altri framework.

Ho avuto la stessa domanda e sono giunto alle stesse conclusioni di base degli altri rispondenti qui:Non preoccuparti di testare l'unità sul livello di comunicazione database effettivo, ma se vuoi testare l'unità sulle funzioni del tuo modello (per assicurarti che estraggano i dati correttamente, li formattino correttamente, ecc.), usa una sorta di origine dati fittizia e test di configurazione per verificare i dati recuperati.

Anch'io trovo che la definizione scarna di unit test non sia adatta a molte attività di sviluppo web.Ma questa pagina descrive alcuni modelli di test unitari più "avanzati" e può aiutare a ispirare alcune idee per applicare i test unitari in varie situazioni:

Modelli di test unitari

Ho spiegato una tecnica che ho utilizzato proprio per questa situazione Qui.

L'idea di base è quella di esercitare ogni metodo nel tuo DAL - affermare i tuoi risultati - e una volta completato ogni test, eseguire il rollback in modo che il database sia pulito (nessun dato spazzatura/test).

L'unico problema che potresti non trovare "fantastico" è che in genere eseguo un intero test CRUD (non puro dal punto di vista del test unitario) ma questo test di integrazione ti consente di vedere il tuo codice di mappatura CRUD + in azione.In questo modo, se si rompe, lo saprai prima di avviare l'applicazione (mi fa risparmiare un sacco di lavoro quando cerco di andare veloce)

Quello che dovresti fare è eseguire i tuoi test da una copia vuota del database che generi da uno script.Puoi eseguire i tuoi test e quindi analizzare i dati per assicurarti che abbiano esattamente quello che dovrebbero dopo l'esecuzione dei test.Quindi elimini semplicemente il database, poiché è usa e getta.Tutto questo può essere automatizzato e può essere considerato un’azione atomica.

Il test del livello dati e il database lascia insieme poche sorprese per il progetto.Ma i test contro il database hanno i suoi problemi, il principale è che stai testando contro lo stato condiviso da molti test.Se si inserisce una riga nel database in un test, il test successivo può anche vedere quella riga.
Ciò di cui hai bisogno è un modo per ripristinare le modifiche apportate al database.
IL TransactionScope La classe è abbastanza intelligente da gestire transazioni molto complicate, nonché transazioni nidificate in cui il codice in base alle chiamate di prova si impegna sulla propria transazione locale.Ecco un semplice pezzo di codice che mostra quanto sia facile aggiungere la capacità di rollback ai tuoi test:

    [TestFixture]
    public class TrannsactionScopeTests
    {
        private TransactionScope trans = null;

        [SetUp]
        public void SetUp()
        {
            trans = new TransactionScope(TransactionScopeOption.Required);
        }

        [TearDown]
        public void TearDown()
        {
            trans.Dispose();
        }

        [Test]
        public void TestServicedSameTransaction()
        {
            MySimpleClass c = new MySimpleClass();
            long id = c.InsertCategoryStandard("whatever");
            long id2 = c.InsertCategoryStandard("whatever");
            Console.WriteLine("Got id of " + id);
            Console.WriteLine("Got id of " + id2);
            Assert.AreNotEqual(id, id2);
        }
    }

Se stai utilizzando LINQ to SQL come ORM, puoi generare il database al volo (a condizione che tu abbia accesso sufficiente dall'account utilizzato per il test unitario).Vedere http://www.aaron-powell.com/blog.aspx?id=1125

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top