2010-06-21 16 views
15

Modifica: non disponibile JUnit 4 in questo momento.JUnit Exception Testing

Hi there,

Ho una domanda sui test "intelligente" eccezione con JUnit. In questo momento, lo faccio in questo modo:

public void testGet() { 

    SoundFileManager sfm = new SoundFileManager(); 

     // Test adding a sound file and then getting it by id and name. 
     try { 
      SoundFile addedFile = sfm.addSoundfile("E:\\Eclipse_Prj\\pSound\\data\\Adrenaline01.wav"); 
      SoundFile sf = sfm.getSoundfile(addedFile.getID()); 
      assertTrue(sf!=null); 
      System.out.println(sf.toString()); 

      sf = sfm.getSoundfileByName("E:\\Eclipse_Prj\\pSound\\data\\Adrenaline01.wav"); 
      assertTrue(sf!=null); 
      System.out.println(sf.toString()); 
     } catch (RapsManagerException e) { 
      System.out.println(e.getMessage()); 
     } 

     // Test get with invalid id. 
     try { 
      sfm.getSoundfile(-100); 
      fail("Should have raised a RapsManagerException"); 
     } catch (RapsManagerException e) { 
      System.out.println(e.getMessage()); 
     } 

     // Test get by name with invalid name 
     try { 
      sfm.getSoundfileByName(new String()); 
      fail("Should have raised a RapsManagerException"); 
     } catch (RapsManagerException e) { 
      System.out.println(e.getMessage()); 
     } 

    } 

Come potete vedere, ho bisogno di un blocco try/catch per ogni funzione che dovrebbe generare un'eccezione. Sembra non essere un buon modo per farlo - o non c'è possibilità di ridurre l'uso di try/catch?

+0

vorrei suggerire di non farlo System.out.println all'interno di prova. System.out e System.err tendono a mixare e produrre output di test incomprensibili. – RockyMM

risposta

31

Suggerisco che è necessario suddividere lo testGet in più test separati. I singoli blocchi try/catch sembrano essere piuttosto indipendenti l'uno dall'altro. Si consiglia inoltre di estrarre la logica di inizializzazione comune nel proprio metodo di installazione.

Una volta che avete, è possibile utilizzare il supporto eccezione annotazioni di JUnit4, qualcosa di simile:

public class MyTest { 

private SoundManager sfm; 

@Before 
public void setup() { 
     sfm = new SoundFileManager(); 
} 

@Test 
public void getByIdAndName() { 
    // Test adding a sound file and then getting it by id and name. 
     SoundFile addedFile =    
     sfm.addSoundfile("E:\\Eclipse_Prj\\pSound\\data\\Adrenaline01.wav"); 
     SoundFile sf = sfm.getSoundfile(addedFile.getID()); 
     assertTrue(sf!=null); 
     System.out.println(sf.toString()); 

     sf = sfm.getSoundfileByName("E:\\Eclipse_Prj\\pSound\\data\\Adrenaline01.wav"); 
     assertTrue(sf!=null); 
     System.out.println(sf.toString()); 
} 

@Test(expected=RapsManagerException.class) 
public void getByInvalidId() { 
     // Test get with invalid id. 
     sfm.getSoundfile(-100); 
} 

@Test(expected=RapsManagerException.class) 
public void getByInvalidName() { 
     // Test get with invalid id. 
     sfm.getSoundfileByName(new String()); 
} 
} 
+0

Mi piace questo, ma ho paura di passare a JUnit4 in un progetto in esecuzione. – InsertNickHere

+4

@InsertNickHere: la tua attenzione è lodevole, ma in questo caso suggerisco di posizionarla male. JUnit4 può eseguire test in stile JUnit3 senza modifiche, consentendo di migrare gradualmente un test alla volta. Inoltre, JUnit4 ha ora 4 anni, è davvero il momento in cui ti sei trasferito. – skaffman

+1

Devo essere completamente d'accordo con te: in questo caso devo spostarmi su JUnit 4. – InsertNickHere

3

Con JUnit 4, è possibile utilizzare invece le annotazioni. Tuttavia, è necessario separare il test in 3 metodi distinti affinché questo funzioni in modo pulito. Si noti che IMHO che rileva un'eccezione nel primo scenario dovrebbe essere un errore, pertanto ho modificato il blocco catch di conseguenza.

public void testGet() { 
    SoundFileManager sfm = new SoundFileManager(); 

    // Test adding a sound file and then getting it by id and name. 
    try { 
     SoundFile addedFile = sfm.addSoundfile("E:\\Eclipse_Prj\\pSound\\data\\Adrenaline01.wav"); 
     SoundFile sf = sfm.getSoundfile(addedFile.getID()); 
     assertTrue(sf!=null); 
     System.out.println(sf.toString()); 

     sf = sfm.getSoundfileByName("E:\\Eclipse_Prj\\pSound\\data\\Adrenaline01.wav"); 
     assertTrue(sf!=null); 
     System.out.println(sf.toString()); 
    } catch (RapsManagerException e) { 
     fail(e.getMessage()); 
    } 
} 

@Test(expected=RapsManagerException.class) 
public void testGetWithInvalidId() { 
    SoundFileManager sfm = new SoundFileManager(); 

    sfm.getSoundfile(-100); 
} 

@Test(expected=RapsManagerException.class) 
public void testGetWithInvalidName() { 
    SoundFileManager sfm = new SoundFileManager(); 

    sfm.getSoundfileByName(new String()); 
} 
+0

Come ho detto sopra, mi piace l'idea, ma non mi piace passare a JUnit4 in un progetto in esecuzione. – InsertNickHere

+0

@InsertNickHere, dovresti suddividere il test in 3 singoli metodi comunque, con impostazioni comuni. Quindi se vuoi veramente minimizzare il codice di gestione delle eccezioni (e hai un sacco di codice duplicato come quelli che mostri) puoi estrarre il try/catch in un metodo separato e passare il metodo effettivo per essere testato tramite un'interfaccia (ma questo è veramente IMHO eccessivo e rende il tuo codice di test più difficile da capire). –

12

Se si dispone di un'eccezione prevista e non è possibile utilizzare un'annotazione per intrappolare esso, è necessario prenderlo e affermare che hai quello che ti aspettavi. Ad esempio:

Throwable caught = null; 
try { 
    somethingThatThrows(); 
} catch (Throwable t) { 
    caught = t; 
} 
assertNotNull(caught); 
assertSame(FooException.class, caught.getClass()); 

Se è possibile utilizzare un'annotazione, farlo è molto più chiaro. Ma questo non è sempre possibile (ad esempio perché stai testando una sequenza di metodi o perché stai utilizzando JUnit 3).

+0

Scusa ma non vedo alcun vantaggio della tua versione rispetto alla mia. Forse non c'è nessuno con JUnit3 :( – InsertNickHere

+1

Stai mettendo troppo in un caso di test. Stai mettendo troppo in un 'try'. Stai stampando i messaggi per la lettura manuale piuttosto che fare asserzioni di prova fallire!(Stai incorporando percorsi non portatili nel tuo codice, ma questa è la tua attività ...) –

+0

I percorsi sono solo per questo esempio, non invierò qui il 100% del codice originale. Ma grazie per quel consiglio comunque. :) – InsertNickHere

2

La sintassi più concisa è fornita da catch-exception:

public void testGet() { 
    SoundFileManager sfm = new SoundFileManager(); 
    ... // setup sound file manager 

    verifyException(sfm, RapsManagerException.class) 
     .getSoundfile(-100); 

    verifyException(sfm, RapsManagerException.class) 
     .getSoundfileByName(new String()); 
} 
0

In Java 8, è possibile usa le espressioni lambda per ottenere un controllo più stretto su quando viene lanciata l'eccezione. Se si utilizza il metodo delle annotazioni, si asserisce solo che l'eccezione è generata da qualche parte nel metodo di prova. Se stai eseguendo più di una riga di codice nel test, rischi di passare il test quando dovrebbe fallire. La soluzione Java 8 è qualcosa di simile.

static void <T extends Exception> expectException(Class<T> type, Runnable runnable) { 
    try { 
     runnable.run() 
    } catch (Exception ex) { 
     assertTrue(ex.getClass().equals(type)); 
     return; 
    } 
    assertTrue(false); 
} 

Usage:

@Test 
public void test() 
    MyClass foo = new MyClass(); 
    // other setup code here .... 
    expectException(MyException.class,() -> foo.bar()); 
}