Pregunta

He escrito algunas pruebas JUnit con anotación @Test. Si mi método de prueba lanza una excepción comprobada y si quiero hacer valer el mensaje junto con la excepción, hay una manera de hacerlo con la anotación JUnit @Test? Que yo sepa, JUnit 4.7 no proporciona esta característica, pero no todas las versiones futuras se proporcionan? Sé que en .NET se puede afirmar el mensaje y la clase de excepción. Buscando característica similar en el mundo Java.

Esto es lo que quiero:

@Test (expected = RuntimeException.class, message = "Employee ID is null")
public void shouldThrowRuntimeExceptionWhenEmployeeIDisNull() {}
¿Fue útil?

Solución

Se puede utilizar el @Rule anotación con ExpectedException, como este:

@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...
    System.out.println("=======Starting Exception process=======");
    throw new NullPointerException("Employee ID is null");
}

Tenga en cuenta que el ejemplo de los documentos ExpectedException es (actualmente) mal -. No hay constructor público, por lo que tiene que utilizar ExpectedException.none()

Otros consejos

Me gusta el @Rule respuesta. Sin embargo, si por alguna razón usted no desea utilizar 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!");
  }

¿Se tiene que utilizar @Test(expected=SomeException.class)? Cuando tenemos que hacer valer 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());
  }
}

En JUnit 4,13 (una vez liberado) que puede hacer:

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThrows;

...

@Test
void exceptionTesting() {
  IllegalArgumentException exception = assertThrows(
    IllegalArgumentException.class, 
    () -> { throw new IllegalArgumentException("a message"); }
  );

  assertEquals("a message", exception.getMessage());
}

Esto también funciona en 5 pero con diferentes importaciones:

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;

...

En realidad, el mejor uso es con try / catch. ¿Por qué? Porque se puede controlar el lugar donde se espera 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 no está aún probada e incluso si no lanza ninguna excepción pasará la prueba.

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

Raystorm tuvo una buena respuesta. No soy un gran fan de Reglas tampoco. Hago algo similar, excepto que se crea la siguiente clase de utilidad para ayudar a la legibilidad y facilidad de uso, que es uno de los grandes plus'es de anotaciones en el 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();

}

A continuación, 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"
}

Si se utiliza @Rule, el conjunto excepción se aplica a todos los métodos de prueba en la clase de prueba.

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 vuelta dentro de la sentencia catch impide.

@Test (expectedExceptions = ValidationException.class, expectedExceptionsMessageRegExp = "This is not allowed")
public void testInvalidValidation() throws Exception{
     //test code
}
scroll top