2012-01-10 10 views
7

Al revisar la cobertura de mi código observé que muchas pruebas de la unidad no logran verificar finalmente los bloques que intentan cerrar las corrientes de entrada abiertas en los bloques finalmente.Pruebas unitarias de bloques finalmente en Java 6

un ejemplo extracto es:

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

¿Hay alguna solución adecuada para comprobar que todo el interior del bloque finally usando junit4?

Sé que una cobertura de código del 100% no se puede lograr teniendo en cuenta la productividad máxima. Sin embargo, estas líneas rojas son una especie de llamativo en el informe.

Respuesta

6

En primer lugar considerar el uso de IOUtils.closeQuietly(), lo que reducirá su código no probado (y probablemente la duplicación) en:

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

Ahora se hace difícil. La forma "right" sería externalizar la creación de BufferedInputStream en otra clase e inyectar simulacro. Al tener un simulacro, puede verificar si se invocó el método close() apropiado.

@JeffFoster respuesta 's es bastante cerca de lo que quiero decir, sin embargo, recomendaría composición sobre la herencia (a expensas de más código):

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

Dónde fileSystem es una instancia de interfaz de FileSystem con implementación real simple inyectada en el código de producción o simulacro de prueba.

interface FileSystem { 

    InputStream open(String file); 

} 

Otro de los beneficios de la externalización de la apertura de archivos es que si uno decide quitar o añadir búfer cifrado, sólo hay un único lugar de modificar.

Tener esa interfaz se ejemplariza su código de prueba con burla (utilizando 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

Estoy de acuerdo. Encuentro la opción de sobrescribir un método en una prueba muy útil, pero a menudo es un paso intermedio en el camino para elegir la composición. C# es un PITA en ese sentido, ya que los métodos no son virtuales por defecto, así que me parece que a menudo tengo que saltar todo el camino (lo cual es molesto porque quieres trabajar con los cambios más pequeños posibles). –

+0

Gracias eso era exactamente lo que estaba buscando :) Gracias también a Jeff – fyr

5

Se podría refactorizar el código un poco

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

Para algo como esto

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

Y ahora usted puede escribir su prueba para capturar la clase de flujo de entrada y verifique que está cerrado. (el código es áspero, pero espero que tengas la idea general).

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

Si esto vale la pena o no es una pregunta diferente!

0

, debe inyectarse una burlado BufferedInputStream - o crearlo con una fábrica - y cuando el método de la maqueta close() se llama luego tirar un IOException.

Además, no voy a bloquear el bloque anterior hasta que no haya ninguna lógica allí.

0

Creo que debe preguntarse si realmente vale la pena el esfuerzo de prueba. Algunos adictos a las pruebas tienden a perder los rendimientos decrecientes de tratar de lograr ~ 100% de cobertura de prueba.En este caso, parece que algunas de las soluciones propuestas agregan más complejidad al código real para que sea "comprobable". Estoy de acuerdo con el código de prueba complejo, pero agregar complejidad al código real solo para que sea "comprobable" me parece una idea terrible.