2013-03-01 19 views
9

Ho una classe di test parametrizzata con una serie di test unitari che generalmente controllano la creazione di messaggi e-mail personalizzati. In questo momento la classe ha un sacco di test che dipendono dal/i fattore/i utilizzato in classe parametrizzata, il flusso dei test è lo stesso per ogni test. L'esempio di un test:Se dichiarazioni nei test

@Test 
public void testRecipientsCount() { 
    assertEquals(3, recipientsCount); 
} 

ho dovuto aggiungere funcionality in più per la mia classe di posta elettronica che aggiunge alcune e-mail interne in più per l'elenco dei destinatari, e che succede solo per alcuni dei casi e che porta al mio problema .

Diciamo che voglio affermare la quantità di messaggi creati. Per il vecchio test era lo stesso per ogni caso, ma ora è diverso a seconda dei casi. Il modo più intuitivo per me è stato quello di aggiungere istruzioni if:

@Test 
public void testRecipientsCount(){ 
    if(something) { 
     assertEquals(3, recipientsCount); 
    } 
    else { 
     assertEquals(4, recipientsCount); 
    } 
} 
... 

ma il mio più esperto collega dice che dovremmo evitare di IFS a classi di test (e io kinda d'accordo su questo).

Ho pensato che il test di suddivisione su due classi di test potesse funzionare, ma che avrebbe portato a un codice ridondante in entrambe le classi (devo ancora verificare se sono stati creati messaggi non originali, le loro dimensioni, contenuto ecc.) E un poche righe aggiunte per uno di loro.

La mia domanda è: come faccio a fare questo in modo da non usare if o carichi di codice ridondante (non usare la classe parametrizzata produrrebbe un codice ridondante ancora più)?

+0

'aggiunge qualche extra email interne alla lista dei destinatari'. Questa lista di indirizzi interni è stata inserita nella classe? Se è così, basta cancellare l'elenco per la maggior parte dei test e avere uno o due test 'testInternalMail' che compilano tale elenco –

+0

Non penso sia possibile con la classe di test parametrizzata. Sono stati aggiunti i casi di test suggeriti per ogni serie di argomenti e le asserzioni sarebbero diverse per alcuni. La tua soluzione funzionerebbe senza classe Parametrized, ma voglio evitarlo. – Raidmaster

+0

Potresti cambiare il codice postato in una classe di test più completa (ma ancora piccola e falsa), in modo che possiamo capire come impostare i parametri? –

risposta

3

A mio parere un Junit dovrebbe essere letto come un protocollo. Ciò significa che è possibile scrivere codice ridondante per rendere più leggibile il test case. Scrivi una testcase per ogni possibile istruzione if nella tua logica aziendale, nonché i casi negativi. Questo è l'unico modo per ottenere una copertura di prova del 100%. Io uso la struttura:

- testdata preparation 
- executing logic 
- check results 
- clear data 

Inoltre si dovrebbe scrivere complessa afferma di grandi oggetti in classi astratte:

abstract class YourBusinessObjectAssert{ 
    public static void assertYourBussinessObjectIsValid(YourBusinessObject pYourBusinessObject,  Collection<YourBusinessObject> pAllYourBusinessObject) { 
for (YourBusinessObject lYourBusinessObject : pAllYourBusinessObject) { 
    if (lYourBusinessObject.isTechnicalEqual(pYourBusinessObject)) { 
    return; 
    } 
} 
assertFalse("Could not find requested YourBusinessObject in List<YourBusinessObject>!", true); 
} 
} 

Esso contribuirà a ridurre la complessità del codice e si sta rendendo disponibile agli altri sviluppatori.

0

Un test di unità dovrebbe, a mio parere, testare solo una cosa se possibile. Come tale direi che se hai bisogno di un'istruzione if, allora probabilmente hai bisogno di più di un test unitario - uno per ogni blocco nel codice if/else.

Se possibile direi un test dovrebbe leggere come una storia - il mio layout preferito (e non è la mia idea :-) - la sua abbastanza ampiamente utilizzato) è:

- given: do setup etc 
- when: the place you actually execute/call the thing under test 
- expect: verify the result 

Un altro vantaggio di un'unità test di verifica solo una cosa è che quando si verifica un errore è inequivocabile quale sia la causa - se si dispone di un lungo test con molti risultati possibili diventa molto più difficile ragionare perché un test ha fallito.

0

Perché non utilizzare un metodo privato che testa le cose che sono comuni a ciascun metodo? Qualcosa di simile (ma probabilmente con qualche parametro di input per il metodo testCommonStuff()):

@Test 
public void testRecipientsCountA(){ 
    testCommonStuff(); 
    // Assert stuff for test A 
} 

@Test 
public void testRecipientsCountB(){ 
    testCommonStuff(); 
    // Assert stuff for test B 
} 

private void testCommonStuff() { 
    // Assert common stuff 
} 

In questo modo non si ottiene il codice ridondante e si può dividere il test in test più piccoli. Inoltre rendi i tuoi test meno soggetti a errori SE dovrebbero testare le stesse cose.Saprai ancora quale test è fallito, quindi la tracciabilità non dovrebbe essere un problema.

+0

Uso la classe Parametrized e eseguirò entrambi i casi di test per ogni set di argomenti. Il modo corretto è di eseguirne solo uno, a seconda dell'argomento effettivo. – Raidmaster

+0

@ Badman6 Non capisco completamente. Dici "ma questo porterebbe a un codice ridondante in entrambe le classi (devo ancora controllare se sono stati creati messaggi non originali, le loro dimensioni, il contenuto ecc.)". Ciò non significa che hai codice ridondante in uno o più metodi di prova? Se è così, puoi averlo in un metodo privato come sopra e chiamarlo da ciascuno dei metodi che dovrebbero eseguirlo. – Magnilex

+0

Non è quello. Il conteggio dei destinatari dipende da un parametro specificato in un provider di argomenti per la classe di parametri. Se voglio mantenere il codice ridondante della classe Parametrized sarebbe il risultato di dividere i miei test in 2 classi (una per i casi con, uno senza destinatari interni). Per i test, le classi che condividono il codice non sono realmente un'opzione. La soluzione è ok, non solo per la classe parametrizzata – Raidmaster

0

Non sono sicuro se sia possibile fare in modo pulito ciò che si sta cercando in un test parametrizzato. Se hai bisogno di un comportamento di test case diverso in base a quale parametro per alcune funzionalità, potresti semplicemente provare a testare queste caratteristiche separatamente - in diverse classi di test che non sono parametrizzate.

Se davvero si vuole tenere tutto nelle classi di test parametrizzate, sarei incline a fare una funzione di supporto in modo che il test di esempio, almeno si legge come una semplice affermazione:

@Test 
public void testRecipientsCount(){ 
    assertEquals(expectedCount(something), recipientsCount) 
} 

private int expectedCount(boolean which) { 
    if (something){ 
     return 3; 
    } 
    else { 
     return 4; 
    } 
} 
Problemi correlati