Jak Mogę potwierdzić wiadomość o wyjątku za pomocą adnotacji testowej JUnit?

Napisałem kilka testów JUnit z adnotacją @Test. Jeśli moja metoda testowa rzuca zaznaczony wyjątek i jeśli chcę potwierdzić wiadomość wraz z wyjątkiem, czy istnieje sposób na to, aby to zrobić za pomocą adnotacji JUnit @Test? AFAIK, JUnit 4.7 nie zapewnia tej funkcji, ale czy jakieś przyszłe wersje ją zapewniają? Wiem, że w. NET można potwierdzić wiadomość i klasę exception. Szukam podobnej funkcji w świecie Javy.

This is what I want:

@Test (expected = RuntimeException.class, message = "Employee ID is null")
public void shouldThrowRuntimeExceptionWhenEmployeeIDisNull() {}
Author: ROMANIA_engineer, 2010-03-18

12 answers

Możesz użyć @Rule Przypisy ExpectedException, tak:

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

Zauważ, że przykład w ExpectedException docs jest (obecnie) błędny - nie ma konstruktora publicznego, więc musisz użyć ExpectedException.none().

 552
Author: Jesse Merriman,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-09-12 10:41:53

I like the @Rule odpowiedz. Jeśli jednak z jakiegoś powodu nie chcesz używać zasad. Jest trzecia opcja.

@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!");
  }
 51
Author: Raystorm,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:26:36

W JUnit 4.13 możesz zrobić:

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

Działa to również w JUnit 5 , ale z różnymi importami:

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

...
 44
Author: Johan Boberg,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-01-04 12:52:13

Czy musisz używać @Test(expected=SomeException.class)? Kiedy musimy potwierdzić rzeczywiste przesłanie wyjątku, to właśnie robimy.

@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());
  }
}
 32
Author: c_maker,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-11-25 03:56:28

Właściwie, najlepiej jest użyć try/catch. Dlaczego? Ponieważ możesz kontrolować miejsce, w którym spodziewasz się wyjątku.

Rozważ ten przykład:

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

Co jeśli pewnego dnia kod zostanie zmodyfikowany i przygotowanie do testu spowoduje wywołanie RuntimeException? W takim przypadku rzeczywisty test nie jest nawet testowany i nawet jeśli nie rzuci żadnego wyjątku, test przejdzie.

Dlatego o wiele lepiej jest użyć try / catch niż polegać na adnotacji.

 11
Author: Krzysztof Cislo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-08-18 09:18:24

Raystorm miał dobrą odpowiedź. Ja też nie przepadam za zasadami. Robię coś podobnego, z wyjątkiem tego, że tworzę następującą klasę użyteczności, aby pomóc w czytelności i użyteczności, która jest jednym z dużych plusów adnotacji na pierwszym miejscu.

Dodaj tę klasę użyteczności:

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

}

Więc do mojego testu jednostkowego, potrzebuję tylko tego kodu:

@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"
}
 8
Author: user64141,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-08-13 17:39:34

Nigdy nie lubiłem sposobu twierdzenia WYJĄTKÓW z Junit. Jeśli użyję "oczekiwanego" w adnotacji, wydaje mi się, że z mojego punktu widzenia naruszamy wzorzec "given, when, then", ponieważ "then" znajduje się na górze definicji testu.

Ponadto, jeśli używamy "@Rule", mamy do czynienia z tak dużą ilością kodu boilerplate. Tak więc, jeśli możesz zainstalować nowe biblioteki dla swoich testów, sugeruję, aby spojrzeć na AssertJ (ta Biblioteka jest teraz dostarczana z SpringBoot)

Następnie test, który jest nie naruszając zasad "given / when / then", a robi się to za pomocą AssertJ do weryfikacji:

1 - wyjątek jest tym, czego oczekujemy. 2-ma również oczekiwaną wiadomość

Będzie wyglądać tak:

 @Test
void should_throwIllegalUse_when_idNotGiven() {

    //when
    final Throwable raisedException = catchThrowable(() -> getUserDAO.byId(null));

    //then
    assertThat(raisedException).isInstanceOf(IllegalArgumentException.class)
            .hasMessageContaining("Id to fetch is mandatory");
}
 4
Author: geeksusma,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-11-05 15:32:50

Jeśli używasz @Rule, zestaw wyjątków jest stosowany do wszystkich metod testowych w klasie testowej.

 2
Author: Jyothi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-09-26 10:13:14

Podoba mi się odpowiedź user64141, ale okazało się, że może być bardziej uogólniona. Oto moje zdanie:

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

}

Zauważ, że pozostawienie instrukcji "fail" w bloku try powoduje przechwycenie odpowiedniego wyjątku twierdzenia; użycie return w instrukcji catch zapobiega temu.

 1
Author: Addison Crump,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-01-11 13:46:36

Zaimportuj bibliotekę Catch-exception i użyj jej. Jest znacznie czystsza niż ExpectedException lub try-catch.

Przykład z ich dokumentów:

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
Author: whistling_marmot,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-09-29 09:49:12

Wolałbym AssertJ do tego.

        assertThatExceptionOfType(ExpectedException.class)
        .isThrownBy(() -> {
            // method call
        }).withMessage("My message");
 0
Author: VaibS,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-11-04 18:09:20
@Test (expectedExceptions = ValidationException.class, expectedExceptionsMessageRegExp = "This is not allowed")
public void testInvalidValidation() throws Exception{
     //test code
}
 -3
Author: aasha,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-08-02 06:38:02