JUnit 5: Jak potwierdzić zgłoszony wyjątek?

214

Czy istnieje lepszy sposób na stwierdzenie, że metoda zgłasza wyjątek w JUnit 5?

Obecnie muszę użyć @Rule, aby sprawdzić, czy mój test zgłasza wyjątek, ale to nie działa w przypadkach, w których oczekuję, że wiele metod zgłasza wyjątki w moim teście.

steventrouble
źródło
1
możesz sprawdzić AssertJ pod kątem sprawdzania wyjątków, ponieważ jest on bardziej elastyczny niż JUnit5
użytkownik1075613

Odpowiedzi:

323

Możesz użyć assertThrows(), co pozwala przetestować wiele wyjątków w ramach tego samego testu. Dzięki obsłudze lambd w Javie 8 jest to kanoniczny sposób testowania wyjątków w JUnit.

Według dokumentów JUnit :

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

@Test
void exceptionTesting() {
    MyException thrown = assertThrows(
           MyException.class,
           () -> myObject.doThing(),
           "Expected doThing() to throw, but it didn't"
    );

    assertTrue(thrown.getMessage().contains("Stuff"));
}
steventrouble
źródło
11
Ze starej szkoły „Nie wiem dużo o Junit5 i prawdopodobnie za mało o Java8”… wygląda to dość dziwnie. Czy mógłbyś dodać kilka wyjaśnień; na przykład „która część jest testowanym„ kodem produkcyjnym ”… który miałby wyrzucić”?
GhostCat
1
() -> wskazuje na wyrażenie lambda, które akceptuje zero argumentów. Zatem „kod produkcyjny”, który ma wywołać wyjątek, znajduje się w wskazanym bloku kodu (tj. throw new...Instrukcji w nawiasach klamrowych).
Sam Brannen
1
Zazwyczaj ekspresja lambda oddziałuje z badanym podmiotem (SUT). Innymi słowy, bezpośrednie zgłoszenie wyjątku takiego jak powyżej służy wyłącznie do celów demonstracyjnych.
Sam Brannen
1
Wygląda na to, że expectThrows jest przestarzały. Dokumenty mówią teraz, aby teraz używać assertThrows ().
depsypher
5
Od wersji 5.0.0-M4 expectThrows nie jest już dostępny. Dozwolone tylko assertThrows . Zobacz github.com/junit-team/junit5/blob/master/documentation/src/docs/… : „Usunięto przestarzałą metodę Assertions.expectThrows () na rzecz Assertions.assertThrows ()”
gil.fernandes
91

W Javie 8 i JUnit 5 (Jupiter) możemy domagać się wyjątków w następujący sposób. Za pomocąorg.junit.jupiter.api.Assertions.assertThrows

public static <T rozszerza Throwable> T assertThrows (klasa <T> oczekiwanaTyp, wykonywalny plik wykonywalny)

Zapewnia, że ​​wykonanie dostarczonego pliku wykonywalnego zgłasza wyjątek oczekiwany typ i zwraca wyjątek.

Jeśli nie zostanie zgłoszony żaden wyjątek lub zgłoszony zostanie wyjątek innego typu, ta metoda zakończy się niepowodzeniem.

Jeśli nie chcesz wykonywać dodatkowych kontroli instancji wyjątku, po prostu zignoruj ​​wartość zwracaną.

@Test
public void itShouldThrowNullPointerExceptionWhenBlahBlah() {
    assertThrows(NullPointerException.class,
            ()->{
            //do whatever you want to do here
            //ex : objectName.thisMethodShoulThrowNullPointerExceptionForNullParameter(null);
            });
}

Takie podejście wykorzysta interfejs funkcjonalny Executablew org.junit.jupiter.api.

Patrz:

główny
źródło
1
Do góry z tym! To jak dotąd najlepsza odpowiedź, która jest najbardziej aktualna w JUnit 5. Ponadto IntelliJ jeszcze bardziej assertThrows(NoSuchElementException.class, myLinkedList::getFirst);
skraca moc
26

Zmienili to w JUnit 5 (oczekiwany: wyjątek InvalidArgumentException, metoda faktyczna: wywołana), a kod wygląda następująco:

@Test
public void wrongInput() {
    Throwable exception = assertThrows(InvalidArgumentException.class,
            ()->{objectName.yourMethod("WRONG");} );
}
jstar
źródło
21

Teraz Junit5 zapewnia sposób na potwierdzenie wyjątków

Możesz przetestować zarówno wyjątki ogólne, jak i niestandardowe

Ogólny scenariusz wyjątku:

ExpectGeneralException.java

public void validateParameters(Integer param ) {
    if (param == null) {
        throw new NullPointerException("Null parameters are not allowed");
    }
}

ExpectGeneralExceptionTest.java

@Test
@DisplayName("Test assert NullPointerException")
void testGeneralException(TestInfo testInfo) {
    final ExpectGeneralException generalEx = new ExpectGeneralException();

     NullPointerException exception = assertThrows(NullPointerException.class, () -> {
            generalEx.validateParameters(null);
        });
    assertEquals("Null parameters are not allowed", exception.getMessage());
}

Możesz znaleźć próbkę do przetestowania wyjątku tutaj: podaj przykładowy kod wyjątku

ExpectCustomException.java

public String constructErrorMessage(String... args) throws InvalidParameterCountException {
    if(args.length!=3) {
        throw new InvalidParameterCountException("Invalid parametercount: expected=3, passed="+args.length);
    }else {
        String message = "";
        for(String arg: args) {
            message += arg;
        }
        return message;
    }
}

ExpectCustomExceptionTest.java

@Test
@DisplayName("Test assert exception")
void testCustomException(TestInfo testInfo) {
    final ExpectCustomException expectEx = new ExpectCustomException();

     InvalidParameterCountException exception = assertThrows(InvalidParameterCountException.class, () -> {
            expectEx.constructErrorMessage("sample ","error");
        });
    assertEquals("Invalid parametercount: expected=3, passed=2", exception.getMessage());
}
Anupama Boorlagadda
źródło
1
Nie ma różnicy w tym, jak JUnit obsługuje wbudowane i niestandardowe wyjątki.
raindev
9

Myślę, że jest to jeszcze prostszy przykład

List<String> emptyList = new ArrayList<>();
Optional<String> opt2 = emptyList.stream().findFirst();
assertThrows(NoSuchElementException.class, () -> opt2.get());

Wywołanie get()opcjonalnego zawierającego pustego ArrayListrzuci NoSuchElementException. assertThrowsdeklaruje oczekiwany wyjątek i podaje dostawcę lambda (nie przyjmuje argumentów i zwraca wartość).

Dzięki @prime za odpowiedź, którą mam nadzieję opracowałem.

JesseBoyd
źródło
1
metoda assertThrowszwraca zgłoszony wyjątek. Możesz więc zrobić to tak, jak NoSuchElementException e = assertThrows(NoSuchElementException.class, () -> opt2.get());poniżej, możesz wykonać dowolne twierdzenia dotyczące obiektu wyjątku, jaki chcesz.
Captain Man
8

Możesz użyć assertThrows(). Mój przykład pochodzi z dokumentacji http://junit.org/junit5/docs/current/user-guide/

import org.junit.jupiter.api.Test;

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

....

@Test
void exceptionTesting() {
    Throwable exception = assertThrows(IllegalArgumentException.class, () -> {
        throw new IllegalArgumentException("a message");
    });
    assertEquals("a message", exception.getMessage());
}
Will Humphreys
źródło
2

Jeszcze prostsza jedna wkładka. W tym przykładzie nie są wymagane wyrażenia lambda ani nawiasy klamrowe w Javie 8 i JUnit 5

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

@Test
void exceptionTesting() {

    assertThrows(MyException.class, myStackObject::doStackAction, "custom message if assertion fails..."); 

// note, no parenthesis on doStackAction ex ::pop NOT ::pop()
}
rnyunja
źródło
1

Wydaje mi się, że w dokumentacji tego konkretnego przykładu wystąpił błąd. Metodą, która jest zamierzona, jest expectThrows

public static void assertThrows(
public static <T extends Throwable> T expectThrows(
Peter Isberg
źródło
3
„Usunięto przestarzałą metodę Assertions.expectThrows () na korzyść Assertions.assertThrows ().”
Martin Schröder
W przypadku Junita 5 upewnij się, że pochodzi on z org.junit.jupiter.api.Assertions, a nie org.testng.Assert. Nasz projekt zawiera zarówno Junit, jak i TestNG, i ciągle otrzymywałem błąd assertThrows, który zwrócił błąd void, dopóki nie zmieniłem go na assertExpects. Okazało się, że korzystam z org.testng.Assert.
barryku
-5

Oto prosty sposób.

@Test
void exceptionTest() {

   try{
        model.someMethod("invalidInput");
        fail("Exception Expected!");
   }
   catch(SpecificException e){

        assertTrue(true);
   }
   catch(Exception e){
        fail("wrong exception thrown");
   }

}

Udaje się to tylko wtedy, gdy zostanie zgłoszony wyjątek.

kiwicomb123
źródło