2010-03-18 10 views
191

He escrito algunas pruebas JUnit con la anotación @Test. Si mi método de prueba arroja una excepción marcada y si deseo afirmar el mensaje junto con la excepción, ¿hay alguna manera de hacerlo con la anotación JUnit @Test? AFAIK, JUnit 4.7 no proporciona esta característica pero ¿las versiones futuras lo proporcionan? Sé que en .NET puedes afirmar el mensaje y la clase de excepción. Buscando características similares en el mundo de Java.¿Cómo confirmo mi mensaje de excepción con la anotación JUnit Test?

Esto es lo que quiero:

@Test (expected = RuntimeException.class, message = "Employee ID is null") 
public void shouldThrowRuntimeExceptionWhenEmployeeIDisNull() {} 
+1

Ahora que lo pienso un poco más ... ¿Estás seguro de que es una buena idea afirmar el mensaje? Tu pregunta me hizo profundizar un poco en el código fuente de junit y parece que podrían haber agregado fácilmente esta característica. El hecho de que lo hicieron * no *, me hace pensar que podría no considerarse una buena práctica. ¿Por qué es importante en su proyecto afirmar el mensaje? –

+6

buena pregunta. Diga que un método que contiene 15 líneas de código arroja la misma excepción desde 2 lugares diferentes. Mis casos de prueba necesitan afirmar no solo la clase de excepción sino también el mensaje en ella. En un mundo ideal, cualquier comportamiento anormal debería tener su propia excepción. Si ese hubiera sido el caso, mi pregunta nunca surgiría, pero las aplicaciones de producción no tienen su única excepción personalizada para cada comportamiento anormal. – Cshah

Respuesta

333

usted podría utilizar la anotación @Rule con ExpectedException, así:

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

@Test 
public void shouldThrowRuntimeExceptionWhenEmployeeIDisNull() throws Exception { 
    expectedEx.expect(RuntimeException.class); 
    expectedEx.expectMessage("Employee ID is null"); 
    // do something that should throw the exception... 
} 

Tenga en cuenta que el ejemplo de los ExpectedException docs está mal (en la actualidad) - no hay constructor público, así que hay que utilizar ExpectedException.none().

+0

Nota: Para mí cuando se especificó 'expectMessage' como una cadena vacía, la comparación del mensaje no se realizó – redDevil

+1

Útil para mí. Gracias. El método de prueba debería tener 'throws RuntimeException' después de agregar el código que arroja una excepción. No lo veo ... – Bumbolt

+2

Personalmente, no me gustaría utilizarlo ya que la creación de campos con un pequeño subconjunto de métodos es una mala práctica. No es una crítica de la respuesta, sino del diseño de JUnit. La solución hipotética del PO sería mucho mejor si existiera. –

20

¿Tiene que usar @Test(expected=SomeException.class)? Cuando tenemos que afirmar el mensaje real de la excepción, esto es lo que hacemos.

@Test 
public void myTestMethod() 
{ 
    try 
    { 
    final Integer employeeId = null; 
    new Employee(employeeId); 
    fail("Should have thrown SomeException but did not!"); 
    } 
    catch(final SomeException e) 
    { 
    final String msg = "Employee ID is null"; 
    assertEquals(msg, e.getMessage()); 
    } 
} 
+5

Soy consciente de escribir un bloque catch y usar assert dentro de eso, pero para una mejor legibilidad del código quiero hacer anotaciones. – Cshah

+0

Además, no recibirá un mensaje tan agradable como cuando lo hace de la manera "correcta". – NeplatnyUdaj

+8

El problema con la versión try/catch, ahora que JUnit proporciona '@Test (expected = ...)' y 'ExpectedException', es que he visto en numerosas ocasiones a alguien _olvidando poner la llamada a' fail() ' al final del bloque 'try'. Si no es atrapado por la revisión del código, su prueba puede ser falsamente positiva y siempre pasar. –

2

Si usa @Rule, el conjunto de excepciones se aplica a todos los métodos de prueba en la clase de prueba.

+2

Usando Jesse Merriman respuesta, la excepción solo se verifica en los métodos de prueba que llaman a expectedEx.expect() y expectedEx.expectMessage(). Los otros métodos usarán la definición expectedEx = ExpectedException.none(), es decir, no se espera una excepción. – Egl

20

Me gusta el @Rule respuesta. Sin embargo, si por alguna razón no quiere usar reglas. Hay una tercera opción.

@Test (expected = RuntimeException.class) 
public void myTestMethod() 
{ 
    try 
    { 
     //Run exception throwing operation here 
    } 
    catch(RuntimeException re) 
    { 
     String message = "Employee ID is null"; 
     assertEquals(message, re.getMessage()); 
     throw re; 
    } 
    fail("Employee Id Null exception did not throw!"); 
    } 
+6

Esto es fe feo – Danon

+0

¿Esta prueba no siempre fallará? el error está fuera del bloque catch. –

+2

@stolen_leaves Solo si el código en el bloque catch no arroja una excepción. – Raystorm

6

Raystorm tenía una buena respuesta. Tampoco soy un gran admirador de Reglas. Hago algo similar, excepto que creo la siguiente clase de utilidad para ayudar a la legibilidad y la facilidad de uso, que es una de las grandes ventajas de las anotaciones en primer lugar.

Añadir esta clase de utilidad:

import org.junit.Assert; 

public abstract class ExpectedRuntimeExceptionAsserter { 

    private String expectedExceptionMessage; 

    public ExpectedRuntimeExceptionAsserter(String expectedExceptionMessage) { 
     this.expectedExceptionMessage = expectedExceptionMessage; 
    } 

    public final void run(){ 
     try{ 
      expectException(); 
      Assert.fail(String.format("Expected a RuntimeException '%s'", expectedExceptionMessage)); 
     } catch (RuntimeException e){ 
      Assert.assertEquals("RuntimeException caught, but unexpected message", expectedExceptionMessage, e.getMessage()); 
     } 
    } 

    protected abstract void expectException(); 

} 

Entonces para mi prueba de unidad, todo lo que necesito es este código:

@Test 
public void verifyAnonymousUserCantAccessPrivilegedResourceTest(){ 
    new ExpectedRuntimeExceptionAsserter("anonymous user can't access privileged resource"){ 
     @Override 
     protected void expectException() { 
      throw new RuntimeException("anonymous user can't access privileged resource"); 
     } 
    }.run(); //passes test; expected exception is caught, and this @Test returns normally as "Passed" 
} 
4

En realidad, el mejor uso es con try/catch. ¿Por qué? Porque puedes controlar el lugar donde esperas la excepción.

Considere este ejemplo:

@Test (expected = RuntimeException.class) 
public void someTest() { 
    // test preparation 
    // actual test 
} 

¿Qué pasa si un día el código se modifica y preparación de la prueba arrojará un RuntimeException? En ese caso, la prueba real ni siquiera se prueba e incluso si no arroja ninguna excepción, la prueba pasará.

Es por eso que es mucho mejor usar try/catch que confiar en la anotación.

+0

Lamentablemente, esta es mi respuesta también. –

+1

Las preocupaciones con respecto a los cambios en el código se alivian al tener casos de prueba pequeños, específicos de la permutación. A veces es inevitable y tenemos que confiar en el método catch/try, pero si eso sucede con frecuencia, entonces es probable que tengamos que revisar la forma en que escribimos nuestras funciones de casos de prueba. –

+0

Eso es un problema con su prueba y/o código. NO espera una RuntimeException general, espera una excepción específica o, como mínimo, un mensaje específico. –

0

Importe la biblioteca catch-exception y úselo. Es mucho más limpio que la regla ExpectedException o try-catch.

Ejemplo forman sus documentos:

import static com.googlecode.catchexception.CatchException.*; 
import static com.googlecode.catchexception.apis.CatchExceptionHamcrestMatchers.*; 

// given: an empty list 
List myList = new ArrayList(); 

// when: we try to get the first element of the list 
catchException(myList).get(1); 

// then: we expect an IndexOutOfBoundsException with message "Index: 1, Size: 0" 
assertThat(caughtException(), 
    allOf(
    instanceOf(IndexOutOfBoundsException.class), 
    hasMessage("Index: 1, Size: 0"), 
    hasNoCause() 
) 
); 
0

me gusta la respuesta de user64141 pero encontró que podría ser más generalizada.Esta es mi opinión:

public abstract class ExpectedThrowableAsserter implements Runnable { 

    private final Class<? extends Throwable> throwableClass; 
    private final String expectedExceptionMessage; 

    protected ExpectedThrowableAsserter(Class<? extends Throwable> throwableClass, String expectedExceptionMessage) { 
     this.throwableClass = throwableClass; 
     this.expectedExceptionMessage = expectedExceptionMessage; 
    } 

    public final void run() { 
     try { 
      expectException(); 
     } catch (Throwable e) { 
      assertTrue(String.format("Caught unexpected %s", e.getClass().getSimpleName()), throwableClass.isInstance(e)); 
      assertEquals(String.format("%s caught, but unexpected message", throwableClass.getSimpleName()), expectedExceptionMessage, e.getMessage()); 
      return; 
     } 
     fail(String.format("Expected %s, but no exception was thrown.", throwableClass.getSimpleName())); 
    } 

    protected abstract void expectException(); 

} 

Tenga en cuenta que salir de la "falla" declaración dentro del bloque try provoca la excepción afirmación relacionada a ser capturado; el uso de return dentro de la declaración catch lo impide.

Cuestiones relacionadas