Test delle unità di divisione per requisito o metodo

16

Innanzitutto, mi scuso per il titolo, non ho potuto pensare al modo più semplice per spiegarlo!

Ho un metodo per cui voglio scrivere test unitari. Lo terrò abbastanza generico perché non voglio discutere l'implementazione del metodo, ma solo il test. Il metodo è:

public void HandleItem(item a)
{         
     CreateNewItem();
     UpdateStatusOnPreviousItem();
     SetNextRunDate();
}

Quindi questa classe ha un metodo pubblico che chiama alcuni metodi privati per eseguire la logica.

Quindi quando scrivo il test unitario voglio controllare che siano state fatte tutte e tre le cose. Dato che sono tutti chiamati nello stesso percorso, ho pensato che avrei potuto farlo come un test:

public void GivenItem_WhenRun_Thenxxxxx
{
     HandleItem(item);
     // Assert item has been created
     // Assert status has been set on the previous item
     // Assert run date has been set
}

Ma pensavo di poterlo scrivere anche come tre test separati:

public void GivenItem_WhenRun_ThenItemIsCreated()
{
    HandleItem(item);
}

public void GivenItem_WhenRun_ThenStatusIsUpdatedOnPreviousItem()
{
   HandleItem(item);
}

public void GivenItem_WhenRun_ThenRunDateIsSet()
{
     HandleItem(item);
}

Quindi per me sembra più bello in quanto essenzialmente elenca i requisiti, ma poi tutti e tre sono correlati e richiedono esattamente lo stesso lavoro eseguito sul metodo testato, quindi sto eseguendo lo stesso codice 3 volte.

Esiste un approccio raccomandato da prendere con questo?

Grazie

    
posta ADringer 24.04.2017 - 09:57
fonte

6 risposte

29

C'è una sottile differenza tra entrambi gli approcci. Nel primo caso, quando il primo Assert fallisce, gli altri due non vengono più eseguiti. Nel secondo caso, tutti e tre i test vengono sempre eseguiti, anche se uno fallisce. A seconda della natura della funzionalità testata, questo potrebbe adattarsi o non adattarsi bene al tuo caso:

  • se ha senso eseguire i tre asserzioni indipendentemente da un altro, perché quando uno fallisce, gli altri due potrebbero non fallire, quindi il secondo approccio ha il vantaggio di ottenere i risultati completi dei test per tutti e 3 i test in una corsa. Questo può essere utile se hai tempi di compilazione notevoli, dato che ti dà la possibilità di correggere fino a 3 errori contemporaneamente prima di fare la prossima build.

  • se, tuttavia, un errore del primo test implicherà sempre che anche gli altri due test falliranno, allora probabilmente è meglio usare il primo approccio (poiché non ha molto senso eseguire un test se lo sai già prima che fallirà).

risposta data 24.04.2017 - 13:28
fonte
11

Risposta breve: è molto più importante che i tuoi test coprano tutte le funzionalità di come lo fanno.

Risposta più lunga: se vuoi ancora scegliere tra queste soluzioni in gran parte equivalenti, puoi utilizzare i criteri ausiliari per ciò che è meglio. Ad esempio,

  • readibility: se il metodo fa un sacco di cose che non sono strettamente correlate, un test combinato potrebbe essere difficile da capire. Tuttavia, anche il metodo potrebbe essere difficile da capire, quindi forse dovresti rifattorizzare il metodo piuttosto che il test!)
  • efficienza: se l'esecuzione del metodo richiede molto tempo, potrebbe essere un motivo debole per combinare tutti e tre i controlli per risparmiare tempo
  • efficiency2: se l'esecuzione del codice di configurazione del framework richiede molto tempo, potrebbe anche essere un motivo debole per evitare più metodi di test. (Tuttavia, se questo è davvero un problema, probabilmente dovresti correggere o modificare la configurazione del test - i test di regressione perdono molto del loro valore se non puoi eseguirli alla velocità della luce.)
risposta data 24.04.2017 - 10:16
fonte
2

Utilizza una chiamata di metodo con più assert. Ecco perché:

Quando si esegue il test di HandleItem (a), si sta verificando che il metodo abbia portato l'elemento nello stato corretto. Invece di "un assert per test", pensa "un concetto logico per test".

Domanda: Se un CreateNewItem ha esito negativo, ma gli altri due metodi riescono, significa che HandleItem è stato completato correttamente? Sto indovinando no.

Con più affermazioni (con messaggi appropriati) saprai esattamente cosa ha fallito. In genere, si verifica un metodo più volte per più ingressi o stati di input, non per evitare più asserzioni.

IMO, queste domande sono in genere un segno di qualcos'altro. È un segno che HandleItem non è davvero qualcosa che puoi "unit test" dal momento che sembra delegare solo ad altri metodi. Quando stai semplicemente convalidando che HandleItem chiama correttamente altri metodi, diventa più un candidato per l'integrazione (nel qual caso avresti ancora 3 asserzioni).

Potresti considerare di rendere pubblici gli altri 3 metodi e verificarli in modo indipendente. O anche estraendoli in un'altra classe.

    
risposta data 25.04.2017 - 03:32
fonte
0

Utilizza il 2 ° approccio. Con il tuo primo approccio, se il test fallisce, non saprai perché, perché potrebbe essere una delle 3 funzioni che hanno fallito. Con il secondo approccio capirai subito dove si è verificato il problema. È possibile inserire codice duplicato nella funzione di configurazione del test.

    
risposta data 24.04.2017 - 13:39
fonte
-1

IMHO dovresti testare separatamente le tre parti di questo metodo in modo da sapere in modo più specifico dove le cose vanno male quando lo fanno evitando di passare due volte la stessa parte del codice.

    
risposta data 24.04.2017 - 11:14
fonte
-2

Non penso ci sia un strong motivo per scrivere metodi di test separati per il tuo caso d'uso. Se si desidera ottenere i risultati da tutte e tre le condizioni variabili, è possibile testare tutti e tre i loro errori concatenandoli in un string e affermando se la stringa è ancora vuota una volta completato il test. Mantenendoli tutti nello stesso metodo, le condizioni e i messaggi di errore documentano la post-condizione prevista del metodo in un unico posto, anziché suddividerlo in tre metodi che potrebbero essere separati successivamente.

Questo significa che il tuo test singolo avrà più codice, ma se hai così tante post-condizioni che questo è un problema, probabilmente vuoi rifattorizzare i tuoi metodi di test per testare comunque i singoli metodi all'interno di HandleItem .

    
risposta data 24.04.2017 - 14:46
fonte

Leggi altre domande sui tag