Mockito verify after exception Junit 4.10
Asked Answered
B

4

81

I am testing a method with an expected exception. I also need to verify that some cleanup code was called (on a mocked object) after the exception is thrown, but it looks like that verification is being ignored. Here is the code. I am using the Junit ExpectedException Rule to verify the expected exception.

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

It seems like the verify is totally being ignored. No matter what method I put in the verify, my test is passing, which is not what I want.

Any idea why thats happenning?

Barbarize answered 5/11, 2012 at 0:0 Comment(0)
I
92

ExpectedException works by wrapping your entire test method in a try-catch block via a JUnit @Rule. When your code throws an exception, it goes up the stack to the nearest try/catch, which happens to be in the ExpectedException instance (which checks that it is the exception you're expecting).

In Java, if an uncaught exception occurs in a method, control will never return to statements later in that method. The same rules apply here: Control never returns to the statements in your test after the exception.

Technically, you could put the verifications in a finally block, but that tends to be a bad habit. EDIT: Your system-under-test might throw an unexpected exception, or no exception at all, which would give you a helpful failure message and trace; however, if that failure then causes your verifications or assertions to fail in the finally block, then Java will show that rather than a message about the unexpected exception or unexpected success. This can make debugging difficult, especially because your error will come from lines of code following the root cause of the error, incorrectly implying that the code above it succeeded.

If you really need to verify state after the exception, on a per-method basis, you can always revert back to this idiom:

@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: With Java 8's lambda expressions, you can wrap a functional interface call in a try block concisely enough to be useful. I imagine support for this syntax will find its way into many standard testing libraries.

assertThrows(MyException.class,
    () -> systemUnderTest.throwingMethod());
Ivonneivor answered 5/11, 2012 at 0:48 Comment(3)
Why would putting the verification in a finally block swallow the exception? finally doesn't catch the exception, so it will be propagated to the wrapper. See Kevin Welker's answer; this works just fine.Withy
@StefanDeitmer I apologize for not articulating it well. If your test is passing, yes, it'll work fine. If the test is failing, it may come from no exception being thrown, OR the wrong exception being thrown, OR the failure of a postcondition state that the answer is talking about. The trouble is that a failed verification amounts to throwing an exception in a finally block, which would keep you from seeing and debugging any unexpected exceptions the method-under-test may throw. That unexpected exception is the swallowed one I mean.Ivonneivor
Beautiful Lambda idiom, thank you!Arnone
S
14

Once Exception is thrown in UT, all the code below that will be ignored.

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

To counter this and verify all the calls made, we can use try with finally.

@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(...));
          }
} 
Shipley answered 1/5, 2019 at 16:24 Comment(0)
K
5

More elegant solution with 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(...));
}
Keyhole answered 21/11, 2013 at 21:45 Comment(2)
Hi, could you expand on what then(caughtException()) is? I have to assume that when and verify are actually Mockito.when and Mockito.verify but you don't specify the class of then.Gunning
this was something like this github.com/Codearte/catch-exception/blob/legacy/1.x/… ,but this is now deprecated. Try CatchException2 or AssertJKeyhole
H
4

I haven't tried this yet, but in addition to Jeff Bowman's excellent answer, you might have the choice to use the ExpectedException Rule with a try... finally construct, placing your verify statement in the finally block.

Hyposensitize answered 5/11, 2012 at 2:59 Comment(4)
Check my middle paragraph...I think that would work but would swallow the ExpectedException message. Thanks for the compliment though!Ivonneivor
Finally works fine for me - executes the verify and still detects the expected exception and it's messageDomino
What do you guys mean when you say "expectedException Rule with try...finally"? How do the two mix?Jabe
@Carl I've updated my answer to explain better. In short, the question is about what the debugging process is in cases where the test fails, and how that may be difficult if the method-under-test throws an unexpected exception or no exception.Ivonneivor

© 2022 - 2024 — McMap. All rights reserved.