2012-01-10 5 views
7

Durante la revisione della copertura del mio codice ho notato che molti test dell'unità non riescono a controllare definitivamente i blocchi che cercano di chiudere gli InputStreams aperti in blocchi alla fine.Il test dell'unità alla fine blocca in Java 6

Un esempio estratto è:

try { 
     f = new BufferedInputStream(new FileInputStream(source)); 
     f.read(buffer); 
    } finally { 
     if (f != null) 
      try { 
       f.close(); 
      } catch (IOException ignored) { 
      } 
     } 
    } 

C'è qualche soluzione adeguata per controllare tutto all'interno del blocco finally utilizzando JUnit4?

So che una copertura del codice del 100% non è realizzabile tenendo a mente la massima produttività. Tuttavia queste linee rosse sono una sorta di accattivante nel report.

risposta

6

Prima di tutto è consigliabile utilizzare IOUtils.closeQuietly(), che ridurrà il vostro codice non testato (e probabilmente la duplicazione) in:

try { 
     f = new BufferedInputStream(new FileInputStream(source)); 
     f.read(buffer); 
    } finally { 
     IOUtils.closeQuietly(f); 
    } 

Ora diventa difficile. Il modo "right" significherebbe esternalizzare la creazione di BufferedInputStream in un'altra classe e iniettare simulazioni. Avendo una simulazione, è possibile verificare se è stato invocato il metodo close() appropriato.

@JeffFoster risposta s' è abbastanza vicino a quello che voglio dire, però mi sento di raccomandare la composizione per l'eredità (a scapito di più codice):

try { 
     f = fileSystem.open(source); 
     f.read(buffer); 
    } finally { 
     IOUtils.closeQuietly(f); 
    } 

Dove fileSystem è un'istanza di FileSystem interfaccia con semplice implementazione reale iniettata nel codice di produzione o simulazione per test.

interface FileSystem { 

    InputStream open(String file); 

} 

Un altro vantaggio di esternalizzazione di apertura file è che se si decide di rimuovere un buffer o aggiungere la crittografia, v'è solo un luogo unico da modificare.

Avere che l'interfaccia si istanzia il codice di test con mock (utilizzando Mockito):

//given 
FileSystem fileSystemMock = mock(FileSystem.class); 
InputStream streamMock = mock(InputStream.class); 

given(fileSystemMock.open("file.txt")).willReturn(streamMock); 

//when 
//your code 

//then 
verify(streamMock).close(); 
+0

Sono d'accordo. Trovo molto utile l'opzione di sovrascrivere un metodo in un test, ma spesso è un passaggio intermedio sulla scelta della composizione. C# è un PITA in questo senso poiché i metodi non sono virtuali per impostazione predefinita, quindi trovo che spesso devo saltare tutto il tempo (che è fastidioso come si desidera lavorare con i più piccoli cambiamenti possibili). –

+0

Grazie, era esattamente quello che stavo cercando :) Grazie anche a Jeff – fyr

5

Si potrebbe refactoring del codice un po '

public class TestMe { 
    public void doSomething() { 
    try { 
     f = new BufferedInputStream(new FileInputStream(source)); 
     f.read(buffer); 
    } finally { 
     if (f != null) 
     try { 
      f.close(); 
     } catch (IOException ignored) { } 
    } 
    } 
} 

Per qualcosa di simile

public class TestMe { 
    public void doSomething() { 
    try { 
     f = createStream() 
     f.read(buffer); 
    } finally { 
     if (f != null) 
     try { 
      f.close(); 
     } catch (IOException ignored) { } 
    } 
    } 

    public InputStream createStream() { 
     return new BufferedInputStream(new FileInputStream(source)); 
    } 
} 

E ora si può scrivere il vostro test per catturare la classe flusso di input e verificare che sia chiuso. (il codice è approssimativo, ma si spera che tu abbia un'idea generale).

public void TestSomething() { 
    InputStream foo = mock(InputStream.class); // mock object 
    TestMe testMe = new TestMe() { 
    @Override 
    public InputStream createStream() { 
      return foo; 
    } 
    } 

    testMe.something(); 

    verify(foo.close()); 
} 

Se questo vale o no è una domanda diversa!

0

deve iniettare un deriso BufferedInputStream - o crearlo con una fabbrica - e quando il metodo del finto close() è chiamato poi gettare uno IOException.

Inoltre, non voglio che il blocco finale sopra fino a quando non c'è alcuna logica in là.

0

Penso che sia necessario chiedersi se valga veramente la pena di testare. Alcuni drogati test tendono a perdere i ritorni decrescenti del tentativo di raggiungere una copertura di prova del ~ 100%.In questo caso sembra che alcune delle soluzioni proposte aggiungano più complessità al codice effettivo per renderlo "testabile". Sto bene con un codice di test complesso, ma aggiungere complessità al codice reale solo per renderlo "testabile" mi sembra una pessima idea.

Problemi correlati