web-dev-qa-db-de.com

Mockito nach Ausnahme verifizieren Junit 4.10

Ich teste eine Methode mit einer erwarteten Ausnahme. Ich muss auch überprüfen, ob ein Bereinigungscode (für ein verspottetes Objekt) aufgerufen wurde, nachdem die Ausnahme ausgelöst wurde, aber anscheinend wird die Überprüfung ignoriert. Hier ist der Code. Ich verwende Junit ExpectedExceptionRule, 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 verify völlig ignoriert. Egal welche Methode ich in die verify eingebe, mein Test ist bestanden, was ich nicht will.

Irgendeine Idee, warum das passiert?

62
Eqbal

ExpectedException funktioniert durch mschließen Ihrer gesamten Testmethode in einem Try-Catch-Block über JUnit @Rule . Wenn Ihr Code eine Ausnahme auslöst, wird der Stapel bis zum nächsten Versuch/Fang hochgefahren, der sich zufällig in der ExpectedException-Instanz befindet (wodurch überprüft wird, ob es sich um die von Ihnen erwartete Ausnahme handelt).

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

Technisch gesehen könnten Sie die Überprüfungen in einem Endblock ablegen, aber das ist normalerweise 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 eine Ablaufverfolgung liefert. Wenn dieser Fehler jedoch dazu führt, dass Ihre Überprüfungen oder Zusicherungen im Block finally 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 impliziert, dass der Code darüber erfolgreich war.

Wenn Sie den Status nach der Ausnahmebedingung wirklich nach Methoden überprüfen müssen, können Sie jederzeit auf diese Redewendung zurückgreifen:

@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 funktionalen Schnittstellenaufruf in einen Try-Block einschließen kurz genug, um nützlich zu sein . Ich stelle mir vor, dass die Unterstützung dieser Syntax Eingang in viele Standard-Testbibliotheken finden wird.

assertThrows(MyException.class,
    () -> systemUnderTest.throwingMethod());
71
Jeff Bowman

Elegantere Lösung mit catch-exception

@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(...));
}
5
MariuszS

Ich habe dies noch nicht ausprobiert, 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 verify-Anweisung in den finally-Block zu setzen.

4
Kevin Welker

Sobald Exception in UT ausgelöst wurde, wird der gesamte Code darunter 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 mit 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(...));
          }
} 
2
singh30