Perché ho bisogno di test unitari per testare i metodi del repository?

15

Ho bisogno di recitare un po 'il diavolo su questa domanda perché non posso difenderlo bene a causa della mancanza di esperienza. Ecco l'accordo, ottengo concettualmente le differenze tra test di unità e test di integrazione. Quando si concentra in particolare sui metodi di persistenza e sul repository, un test unitario utilizza un simulatore possibilmente tramite un framework come Moq per affermare che dire che un ordine cercato è stato restituito come previsto.

Diciamo che ho costruito il seguente test unitario:

[TestMethod]
public void GetOrderByIDTest()
{
   //Uses Moq for dependency for getting order to make sure 
   //ID I set up in 'Arrange' is same one returned to test in 'Assertion'
}

Quindi, se imposto OrderIdExpected = 5 e il mio oggetto mock restituisce 5 come l'ID che il mio test supererà. Capisco. Ho provato l'unità il codice per assicurarmi che il mio codice preforma restituisca l'oggetto e l'ID previsti e non qualcos'altro.

L'argomento che otterrò è questo:

"Why not just skip the unit tests and do integration tests? It's testing the database stored procedure and your code together that's important. It seems like too much extra work to have unit tests and integration tests when ultimately I want to know if the database calls and the code work. I know the tests take longer, but they have to be run and tested regardless so it seems pointless to me to have both. Just test against what matters."

Potrei difenderlo con una definizione di libro di testo come: "Beh, questo è un test di integrazione e dobbiamo testare il codice separatamente come test unitario e, yada, yada, yada ..." Questo è un caso in cui una spiegazione purista delle pratiche contro la realtà sta perdendo. A volte mi imbatto in questo e se non posso difendere il ragionamento dietro il codice di test unitario che alla fine si basa su dipendenze esterne, allora non posso spiegarlo.

Qualsiasi aiuto su questa domanda è molto apprezzato, grazie!

    
posta atconway 30.01.2013 - 21:21
fonte

6 risposte

15

Test unitari e test di integrazione hanno scopi diversi.

I test unitari verificano la funzionalità del tuo codice ... che ottieni ciò che ti aspetti dal metodo quando lo chiami. I test di integrazione testano il comportamento del codice quando combinati insieme come sistema. Non ti aspetti che i test unitari valuti il comportamento del sistema, né ti aspetteresti che i test di integrazione verifichino specifici output di un particolare metodo. p>

I test unitari, se eseguiti correttamente, sono più facili da configurare rispetto ai test di integrazione. Se ti affidi esclusivamente ai test di integrazione, il tuo test sarà:

  1. Sii più difficile scrivere, nel complesso,
  2. Sii più fragile, a causa di tutte le dipendenze richieste e
  3. Offri meno copertura del codice.

Conclusione: utilizzare i test di integrazione per verificare che le connessioni tra gli oggetti funzionino correttamente, ma affidarsi all'analisi dell'unità prima per verificare i requisiti funzionali.

Tutto ciò detto, potresti non aver bisogno di test dell'unità per determinati metodi di repository. Il test delle unità non dovrebbe essere fatto con metodi banali ; se tutto quello che stai facendo è passare una richiesta di un oggetto al codice generato da ORM e restituire un risultato, nella maggior parte dei casi non è necessario testare l'unità; un test di integrazione è adeguato.

    
risposta data 30.01.2013 - 21:26
fonte
12

Sono dalla parte dei pragmatici. Non testare il codice due volte.

Scriviamo solo test di integrazione per i nostri repository. Questi test dipendono solo da una semplice configurazione di test eseguita su un database in memoria. Penso che forniscano tutto ciò che fa un test unitario e altro ancora.

  1. Possono sostituire i test unitari quando fanno TDD. Mentre c'è ancora dell'altro codice di prova da scrivere prima di poter iniziare con il codice reale, funziona molto bene con l'approccio red / green / refactor una volta che tutto è a posto.
  2. Testano il codice reale del repository - il codice contenuto nelle stringhe SQL o nei comandi ORM. È più importante verificare che la query sia corretta piuttosto che verificare che tu abbia effettivamente inviato qualche stringa a un Executioner.
  3. Sono eccellenti come test di regressione. Se falliscono, è sempre dovuto a un problema reale, come una modifica dello schema che non è stata considerata.
  4. Sono indispensabili quando si modifica lo schema del database. Puoi essere sicuro di non aver cambiato lo schema in un modo che interrompe l'applicazione fino al passaggio del test. (Il test dell'unità è inutile in questo caso, perché quando la stored procedure non esiste più, il test dell'unità continuerà a passare.)
risposta data 30.01.2013 - 23:14
fonte
4

I test unitari forniscono un livello di modularità che i test di integrazione (in base alla progettazione) non possono. Quando un sistema viene refactored o ricomposto, (e questo will accade) i test di unità possono essere spesso riutilizzati, mentre i test di integrazione devono spesso essere riscritti. I test di integrazione che cercano di eseguire il lavoro di qualcosa che dovrebbe essere sottoposto a un test unitario spesso stanno facendo troppo, il che li rende difficili da mantenere.

Inoltre, il test dell'unità include i seguenti vantaggi:

  1. I test unitari consentono di decomporre rapidamente un test di integrazione in errore (probabilmente a causa di un bug di regressione) e identificare la causa. Inoltre, questo comunicherà il problema all'intero team più rapidamente di diagrammi o altra documentazione.
  2. I test unitari possono servire come esempi e documentazione (un tipo di documentazione che effettivamente compila ) insieme al tuo codice. A differenza di altri documenti, lo saprai immediatamente quando non è aggiornato.
  3. I test unitari possono servire come indicatori di prestazioni di base quando si tenta di scomporre problemi di prestazioni più grandi, mentre i test di integrazione tendono a richiedere molti strumenti per trovare il problema.

È possibile dividere il lavoro (e applicare un approccio DRY) mentre si usano i test di unità e di integrazione. Basta fare affidamento sui test unitari per piccole unità di funzionalità e non ripetere alcuna logica già presente in un test unitario in un test di integrazione. Questo spesso porta a meno lavoro (e quindi meno ri-lavoro).

    
risposta data 30.01.2013 - 21:51
fonte
4

I test sono utili quando si rompono: in modo proattivo o riattivato.

I test delle unità sono proattivi e possono essere una verifica funzionale continua mentre i test di integrazione sono reattivi su dati falsi / falsi.

Se il sistema in esame è vicino / dipendente dai dati più della logica di calcolo, i test di integrazione dovrebbero acquisire maggiore importanza.

Ad esempio, se stiamo costruendo un sistema ETL, i test più rilevanti riguarderanno i dati (messo in scena, simulato o dal vivo). Lo stesso sistema avrà alcuni test unitari, ma solo attorno a convalida, filtraggio ecc.

Lo schema del repository non deve avere logica computazionale o di business. È molto vicino al database. Ma il repository è anche vicino alla logica di business che lo utilizza. Quindi si tratta di colpire un SALDO.

I test delle unità possono verificare come si comporta il repository. I test di integrazione sono in grado di testare ciò che è VERAMENTE ACCADUTO quando è stato chiamato il repository.

Ora, "COSA HA REALMENTE ACCADUTO" potrebbe sembrare molto allettante. Ma questo potrebbe essere molto costoso per essere eseguito costantemente e ripetutamente. La definizione classica dei test fragili si applica qui.

Quindi la maggior parte delle volte, trovo sufficiente scrivere un test unitario su un oggetto che utilizza il repository. In questo modo verifichiamo se è stato chiamato il giusto metodo di deposito e vengono restituiti i risultati Mocked appropriati.

    
risposta data 29.06.2015 - 01:32
fonte
2

Ci sono 3 cose separate che devono essere testate: la stored procedure, il codice che chiama la stored procedure (ad esempio la classe del repository) e il consumatore. Il lavoro del repository consiste nel generare una query e convertire il set di dati restituito in un oggetto dominio. C'è abbastanza codice per supportare i test delle unità che si separano dall'esecuzione effettiva della query e dalla creazione del set di dati.

Quindi (questo è un esempio molto molto semplificato):

interface IOrderRepository
{
    Order GetOrderByID(Guid id);
}

class OrderRepository : IOrderRepository
{
    private readonly ISqlExecutor sqlExecutor;
    public OrderRepository(ISqlExecutor sqlExecutor)
    {
        this.sqlExecutor = sqlExecutor;
    }

    public Order GetOrderByID(Guid id)
    {
        var sql = "SELECT blah, blah FROM Order WHERE OrderId = @p0";
        var dataset = this.sqlExecutor.Execute(sql, p0 = id);
        var result = this.orderFromDataset(dataset);
        return result;
    }
}

Quindi quando testate OrderRepository , passate un% co_de deriso e controllate che l'oggetto sotto test passi nell'SQL corretto (che è il suo lavoro) e restituisca un oggetto ISqlExecutor corretto dato un dataset di risultati (anche preso in giro) . Probabilmente l'unico modo per testare la classe Order concreta è con test di integrazione, abbastanza giusto, ma si tratta di una classe di wrapper sottile e raramente cambierà, quindi un grosso problema.

Devi ancora testare le tue procedure memorizzate.

    
risposta data 30.01.2013 - 22:21
fonte
0

Posso ridurlo a una linea di pensiero molto semplice: Favorisci i test unitari perché aiuta a localizzare i bug. E fallo coerentemente perché le incoerenze causano confusione.

Altre ragioni derivano dalle stesse regole che guidano lo sviluppo di OO poiché si applicano anche ai test. Ad esempio, il principio della responsabilità unica.

Se alcuni test unitari sembrano non valere la pena, allora forse è un segno che il soggetto non vale la pena avere. O forse la sua funzionalità è più astratta della sua controparte, e i test per esso (così come il suo codice) possono essere astratti a un livello più alto.

Come con qualsiasi cosa, ci sono delle eccezioni. E la programmazione è ancora un po 'una forma d'arte, quindi molti problemi sono abbastanza diversi da giustificare la valutazione di ciascuno per l'approccio migliore.

    
risposta data 22.02.2016 - 21:09
fonte

Leggi altre domande sui tag