2012-01-10 1 views
7

Lors de l'examen de ma couverture de code, j'ai remarqué que beaucoup de tests unitaires ne vérifiaient pas les blocs qui tentent de fermer les InputStreams ouverts dans les blocs finally.Les tests unitaires bloquent finalement en Java 6

Un extrait est Exemple:

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

est-il une solution appropriée pour vérifier tout à l'intérieur du bloc enfin en utilisant junit4?

Je sais qu'une couverture de code de 100% n'est pas réalisable tout en gardant à l'esprit une productivité maximale. Cependant, ces lignes rouges sont en quelque sorte un attrape-regard dans le rapport.

Répondre

6

d'abord envisager d'utiliser IOUtils.closeQuietly(), ce qui réduira votre code non testé (et probablement le double emploi) dans:

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

Maintenant, il devient difficile. Le "right" serait d'externaliser la création de BufferedInputStream dans une autre classe et injecter faux. Avoir un simulacre, vous pouvez vérifier si la méthode close() appropriée a été appelée.

@JeffFoster réponse de est assez proche de ce que je veux dire, mais je recommande la composition de l'héritage (au détriment de plus de code):

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

fileSystem est une instance de l'interface FileSystem avec implémentation réelle simple injectée dans le code de production ou simulation de test.

interface FileSystem { 

    InputStream open(String file); 

} 

Un autre avantage de l'extériorisation ouverture de fichier est que si vous décidez de supprimer un tampon ou ajouter un cryptage, il y a juste un seul endroit à modifier.

Avoir cette interface vous instanciez votre code de test avec simulacres (en utilisant 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

Je suis d'accord. Je trouve l'option de simplement surcharger une méthode dans un test très utile, mais c'est souvent une étape intermédiaire sur le chemin du choix de la composition. C# est un PITA à cet égard car les méthodes ne sont pas virtuelles par défaut donc je trouve que je dois souvent sauter tout le chemin (ce qui est gênant car vous voulez travailler avec les plus petits changements possibles). –

+0

Merci, c'était exactement ce que je cherchais :) Merci aussi à Jeff – fyr

5

Vous pouvez factoriser le code peu

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) { } 
    } 
    } 
} 

Pour quelque chose comme ça

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)); 
    } 
} 

Et maintenant, vous pouvez écrire votre test pour capturer la classe de flux d'entrée et vérifier qui est fermée. (Le code est approximatif, mais j'espère que vous aurez l'idée générale).

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()); 
} 

Que cela en vaille la peine ou pas est une question différente!

0

Vous devriez injecter un moqué BufferedInputStream - ou créer avec une usine - et lorsque la méthode de simulation close() est appelée alors jeter un IOException.

En outre, je ne vais pas que le bloc finalement ci-dessus jusqu'à ce que vous n'avez aucune logique là-bas.

0

Je pense que vous devez vous demander si cela vaut vraiment la peine d'être testé. Certains drogués de test ont tendance à manquer les rendements décroissants d'essayer d'atteindre une couverture de test de ~ 100%.Dans ce cas, il semble que certaines des solutions proposées ajoutent plus complexité au code réel afin de le rendre "testable". Je vais bien avec un code de test complexe, mais ajouter de la complexité au code réel juste pour le rendre «testable» me semble être une idée terrible.