Mockito nach Ausnahme Junit 4.10 überprüfen

72

Ich teste eine Methode mit einer erwarteten Ausnahme. Ich muss auch überprüfen, ob nach dem Auslösen der Ausnahme ein Bereinigungscode (für ein verspottetes Objekt) aufgerufen wurde, aber es sieht so aus, als würde die Überprüfung ignoriert. Hier ist der Code. Ich verwende Junit ExpectedException Rule, um die erwartete Ausnahme zu überprüfen.

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

@Test
public void testExpectedException()
{
   MockedObject mockObj = mock(MockedObj.class);
   MySubject subject = new MySubject(mockedObj);
   expectedEx.expect(MyException.class);
   expectedEx.expectMessage("My exception message.");
   subject.someMethodThrowingException();
   verify(mockObj).
       someCleanup(eq(...));
}

Es scheint, als würde das verifyvöllig ignoriert. Egal welche Methode ich einsetze verify, mein Test besteht, was ich nicht will.

Irgendeine Idee, warum das passiert?

Gl
quelle

Antworten:

84

ExpectedExceptionfunktioniert, indem Sie Ihre gesamte Testmethode über eine JUnit @Rule in einen Try-Catch-Block einschließen . Wenn Ihr Code eine Ausnahme auslöst, geht er den Stapel zum nächsten try / catch hoch, der sich zufällig in der ExpectedException-Instanz befindet (die überprüft, ob es sich um die erwartete Ausnahme handelt).

Wenn in Java eine nicht erfasste Ausnahme in einer Methode auftritt, kehrt das Steuerelement später in dieser Methode niemals zu Anweisungen zurück. Hier gelten die gleichen Regeln: Control kehrt nach der Ausnahme nie mehr zu den Anweisungen in Ihrem Test zurück.

Technisch gesehen könnten Sie die Überprüfungen in einen endgültigen Block setzen, aber das ist in der Regel eine schlechte Angewohnheit . BEARBEITEN: Ihr zu testendes System löst möglicherweise eine unerwartete oder gar keine Ausnahme aus, die Ihnen eine hilfreiche Fehlermeldung und Ablaufverfolgung liefert. Wenn dieser Fehler jedoch dazu führt, dass Ihre Überprüfungen oder Zusicherungen im finallyBlock fehlschlagen , zeigt Java dies anstelle einer Meldung über die unerwartete Ausnahme oder den unerwarteten Erfolg an. Dies kann das Debuggen erschweren, insbesondere weil Ihr Fehler aus Codezeilen stammt, die der Grundursache des Fehlers folgen, was fälschlicherweise bedeutet, dass der darüber liegende Code erfolgreich war.

Wenn Sie den Status nach der Ausnahme wirklich pro Methode überprüfen müssen, können Sie jederzeit zu dieser Redewendung zurückkehren:

@Test
public void testExpectedException()
{
  MockedObject mockObj = mock(MockedObj.class);
  MySubject subject = new MySubject(mockedObj);
  try {
    subject.someMethodThrowingException();
    fail("Expected MyException.");
  } catch (MyException expected) {
    assertEquals("My exception message.", expected.getMessage());
  }
  verify(mockObj).someCleanup(eq(...));
}

Update: Mit den Lambda-Ausdrücken von Java 8 können Sie einen Funktionsschnittstellenaufruf so kurz in einen try-Block einbinden, dass er nützlich ist . Ich kann mir vorstellen, dass die Unterstützung dieser Syntax in viele Standardtestbibliotheken Eingang findet.

assertThrows(MyException.class,
    () -> systemUnderTest.throwingMethod());
Jeff Bowman
quelle
Warum sollte das Einfügen der Überprüfung in einen finallyBlock die Ausnahme verschlucken? finallyfängt die Ausnahme nicht ab, daher wird sie an den Wrapper weitergegeben. Siehe Kevin Welkers Antwort; das funktioniert gut.
Stefan Frye
@StefanDeitmer Ich entschuldige mich dafür, dass ich es nicht gut artikuliert habe. Wenn Ihr Test erfolgreich ist, funktioniert er einwandfrei. Wenn der Test fehlschlägt, kann dies darauf zurückzuführen sein, dass keine Ausnahme ausgelöst wurde oder die falsche Ausnahme ausgelöst wurde oder dass ein Zustand nach der Bedingung fehlgeschlagen ist, über den in der Antwort gesprochen wird. Das Problem besteht darin, dass eine fehlgeschlagene Überprüfung das Auslösen einer Ausnahme in einem finallyBlock darstellt , wodurch Sie keine unerwarteten Ausnahmen sehen und debuggen können, die die zu testende Methode möglicherweise auslöst. Diese unerwartete Ausnahme ist die verschluckte, die ich meine.
Jeff Bowman
5

Elegantere Lösung mit Fangausnahme

@Test
public void testExpectedException()
{
    MockedObject mockObj = mock(MockedObject.class);
    MySubject subject = new MySubject(mockObj);

    when(subject).someMethodThrowingException();

    then(caughtException())
            .isInstanceOf(MyException.class)
            .hasMessage("My exception message.");

    verify(mockObj).someCleanup(eq(...));
}
MariuszS
quelle
Hallo, könnten Sie erweitern, was then(caughtException())ist? Ich muss davon ausgehen, dass whenund verifytatsächlich Mockito.whenund Mockito.verifyaber Sie nicht die Klasse vonthen.
Navigatron
Das war so etwas wie das github.com/Codearte/catch-exception/blob/legacy/1.x/… , aber das ist jetzt veraltet. Versuchen Sie CatchException2 oder AssertJ
MariuszS
5

Sobald die Ausnahme in UT ausgelöst wird, wird der gesamte darunter liegende Code ignoriert.

@Test(expected = Exception.class)
public void testExpectedException() {
   MockedObject mockObj = mock(MockedObj.class);
   MySubject subject = new MySubject(mockedObj);
   subject.doSomething(); // If this line results in an exception then all the code below this will be ignored.
   subject.someMethodThrowingException();
   verify(mockObj).
       someCleanup(eq(...));
}

Um dem entgegenzuwirken und alle getätigten Anrufe zu überprüfen, können wir try with finally verwenden .

@Test(expected = Exception.class)
    public void testExpectedException() {
          MockedObject mockObj = mock(MockedObj.class);
          MySubject subject = new MySubject(mockedObj);
          try {
               subject.someMethodThrowingException(); 
          } finally {
             verify(mockObj).
             someCleanup(eq(...));
          }
} 
singh30
quelle
4

Ich habe dies noch nicht versucht, aber zusätzlich zu Jeff Bowmans hervorragender Antwort haben Sie möglicherweise die Wahl, die ExpectedException-Regel mit einem try ... finally-Konstrukt zu verwenden und Ihre Verifizierungsanweisung in den finally-Block zu setzen.

Kevin Welker
quelle
Überprüfen Sie meinen mittleren Absatz ... Ich denke, das würde funktionieren, aber die ExpectedException-Nachricht verschlucken. Vielen Dank für das Kompliment!
Jeff Bowman
4
Endlich funktioniert gut für mich - führt die Überprüfung aus und erkennt immer noch die erwartete Ausnahme und die Meldung
Carl Pritchett
Was meint ihr, wenn ihr sagt "Erwartete Ausnahmeregel mit Versuch ... endlich"? Wie mischen sich die beiden?
Drakonli
@ Carl Ich habe meine Antwort aktualisiert, um sie besser zu erklären. Kurz gesagt, die Frage ist, wie der Debugging-Prozess in Fällen abläuft, in denen der Test fehlschlägt, und wie schwierig dies sein kann, wenn die zu testende Methode eine unerwartete oder keine Ausnahme auslöst.
Jeff Bowman