2010-07-22 14 views
29

Como ya sabe, la excepción se produce en situaciones anormales. Entonces, ¿cómo análogo estas excepciones? Siento que es un desafío. Para dichos fragmentos de código:¿Cómo hacer la prueba de unidad para Excepciones?

public String getServerName() { 
    try { 

     InetAddress addr = InetAddress.getLocalHost(); 
     String hostname = addr.getHostName(); 
     return hostname; 
    } 
    catch (Exception e) { 
     e.printStackTrace(); 
     return ""; 
    } 
} 

¿Alguien tiene buenas ideas?

+2

Por curiosidad, ¿por qué es esto una CW? – Uri

+0

Probablemente haya notado que el primer comentario para la mayoría de las preguntas es "debería ser wiki". Éste probablemente tiene más de 6 respuestas válidas, por lo que CW parece razonable. –

Respuesta

29

Otras respuestas han abordado el problema general de cómo escribir una prueba unitaria que comprueba que se produce una excepción. Pero creo que su pregunta realmente está preguntando cómo obtener el código para lanzar la excepción en primer lugar.

Tome su código como ejemplo. Sería muy difícil provocar que su getServerName() lanzara internamente una excepción en el contexto de una prueba de unidad simple. El problema es que, para que se produzca la excepción, el código (normalmente) debe ejecutarse en una máquina cuya red esté rota. Disputar que eso ocurra en una prueba unitaria es probablemente imposible ... necesitaría configurar erróneamente la máquina antes de ejecutar la prueba.

¿Cuál es la respuesta?

  1. En algunos casos, la respuesta simple es solo tomar la decisión pragmática y no buscar una cobertura de prueba total. Tu método es un buen ejemplo. Debería estar claro desde la inspección del código lo que el método realmente hace. Probarlo no va a probar nada (excepto en el siguiente **). Todo lo que está haciendo es mejorar sus recuentos de prueba y números de cobertura de prueba, ninguno de los cuales debe ser el proyecto objetivos.

  2. En otros casos, puede ser conveniente separar el código de bajo nivel donde se genera la excepción y convertirlo en una clase separada. Luego, para probar el código de nivel superior que maneja de la excepción, puede reemplazar la clase con una clase simulada que emitirá las excepciones deseadas.

Aquí está su ejemplo dado este "tratamiento". (Esto es un poco artificial ...)

public interface ILocalDetails { 
    InetAddress getLocalHost() throws UnknownHostException; 
    ... 
} 

public class LocalDetails implements ILocalDetails { 
    public InetAddress getLocalHost() throws UnknownHostException { 
     return InetAddress.getLocalHost(); 
    } 
} 

public class SomeClass { 
    private ILocalDetails local = new LocalDetails(); // or something ... 
    ... 
    public String getServerName() { 
     try { 
      InetAddress addr = local.getLocalHost(); 
      return addr.getHostName(); 
     } 
     catch (Exception e) { 
      e.printStackTrace(); 
      return ""; 
     } 
    } 
} 

ahora a la unidad de prueba de esto, se crea una aplicación "simulacro" de la interfaz ILocalDetails cuya getLocalHost() método lanza la excepción que desee en las condiciones apropiadas. A continuación, crea un texto de unidad para SomeClass.getServerName(), organizando que la instancia de SomeClass utilice una instancia de su clase "simulada" en lugar de la clase normal. (El último bit podría hacerse utilizando un marco de burla, exponiendo un setter para el atributo local o utilizando las API de reflexión.)

Obviamente, tendría que modificar el código para que pueda probarse de esta manera. Y hay límites a lo que puede hacer ... por ejemplo, ahora no puede crear una prueba de unidad para hacer que el método real LocalDetails.getLocalHost() arroje una excepción. Debe juzgar caso por caso si vale la pena el esfuerzo de hacer esto; es decir, ¿el beneficio de la prueba unitaria supera el trabajo (y la complejidad del código adicional) de hacer que la clase sea comprobable de esta manera? (El hecho de que hay un método static en la parte inferior de esta es una gran parte del problema.)

** Hay es un punto hipotético para este tipo de pruebas. En su ejemplo, el hecho de que el código original capte una excepción y devuelva una cadena vacía podría ser ser un error ... dependiendo de cómo se especifique la API del método ... y una prueba de unidad hipotética lo recogería. Sin embargo, en este caso, el error es tan evidente que lo detectarías mientras escribes la prueba de la unidad. Y suponiendo que solucione los errores a medida que los encuentre, la prueba unitaria se vuelve algo redundante. (No es de esperar que alguien reconducir este error en particular ...)

+0

Bien, este es mi deseo. Mock es un método para hacer esto, pero necesita ajustar el código de producción y pagar esfuerzos adicionales. ¿Cree que este método simulado podría detectar algunos errores? – Joseph

58

Puede decirle a junit que el comportamiento correcto es obtener una excepción.

En JUnit 4, que dice así:

@Test(expected = MyExceptionClass.class) 
public void functionUnderTest() { 
    … 
} 
6

Muchos marcos de pruebas unitarias permita que sus pruebas para esperar excepciones como parte de la prueba. JUnit, por ejemplo, allows for this.

@Test (expected=IndexOutOfBoundsException.class) public void elementAt() { 
    int[] intArray = new int[10]; 

    int i = intArray[20]; // Should throw IndexOutOfBoundsException 
} 
14

Bien, hay algunas posibles respuestas aquí.

pruebas para una excepción en sí es fácil

import static org.hamcrest.core.Is.is; 
import static org.junit.Assert.assertThat; 

@Test 
public void TestForException() { 
    try { 
     doSomething(); 
     fail(); 
    } catch (Exception e) { 
     assertThat(e.getMessage(), is("Something bad happened")); 
    } 
} 

alternativa, puede utilizar la anotación Excepción tener en cuenta que se puede esperar una excepción a salir.

Ahora, en cuanto a su ejemplo específico, probar que algo está creando dentro de su método, ya sea nuevo o estáticamente como lo hizo, cuando no tiene forma de interactuar con el objeto es complicado. Normalmente, necesita encapsular ese generador en particular y luego usar burlas para poder anular el comportamiento y generar la excepción que espera.

+0

¿No falta a) en esta línea? 'Assert.That (e.msg, Is (" Lo malo pasó ")' mi experiencia java unittesting es muy pequeña, así que no me atreví a editarla directamente. –

+0

Sí, falta un paréntesis al final allí. – Matt

6

Dado que esta cuestión se encuentra en la comunidad wiki Voy a añadir una nueva para integridad: Puede utilizar ExpectedException en JUnit 4

@Rule 
public ExpectedException thrown= ExpectedException.none(); 

@Test 
public void TestForException(){ 
    thrown.expect(SomeException.class); 
    DoSomething(); 
} 

El ExpectedException hace que la excepción lanzada esté disponible para todos los métodos de prueba.

IS es también posible poner a prueba para un mensaje de error específico:

thrown.expectMessage("Error string"); 

o uso comparadores

thrown.expectMessage(startsWith("Specific start")); 

Esto es más corto y más conveniente que

public void TestForException(){ 
    try{ 
     DoSomething(); 
     Fail(); 
    }catch(Exception e) { 
     Assert.That(e.msg, Is("Bad thing happened")) 
    } 
} 

porque si Olvida el error, la prueba puede resultar en un falso negativo.