TIL: Mocking Exception Throws

Today I learned how to correctly mock exceptions being thrown when using Mockito.

As I’ve mentioned a few times, I’ve been working on improving our unit tests at work. Recently I did some tests where I needed to simulate specific exceptions being thrown in order to validate the exception handling in a particular method. Rather than spend me time trying to actually cause the exceptions to be hit, I figured it would be much easier to just mock the exceptions being thrown on my own.

Initially when I tackled the issue, I used something like the following, where I used the when.thenThrow syntax:

1
when(helper.doSomething()).thenThrow(new IOException());

And this actually worked. I was able to get my exceptions throwing correctly and trigger the bits of code that I wanted to. I commit my changes and moved on.

But today I had to come back to the code again for another effort which required me to merge in the tests with some slightly modified code. To my surprise, the tests completely stopped working. Rather than running, I constantly got the following error being returned to me:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
java.lang.Exception: Unexpected exception, expected<net.fuzzylimes.blog.DummyException> but was<org.mockito.exceptions.base.MockitoException>

    at org.junit.internal.runners.statements.ExpectException.evaluate(ExpectException.java:28)
    at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
    at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
    at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
    at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
    at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
Caused by: org.mockito.exceptions.base.MockitoException:
Checked exception is invalid for this method!

What the heck! It was working just fine before!

After spending some more time actually reading the exception and searching around a bit, I found that the issue is actually caused when you try to throw an exception in a method that doesn’t actually throw that exception. For example, let’s say I have the following code:

1
2
3
4
5
6
7
8
9
   public boolean doSomething() throws DummyException {
       try {
           System.out.println("Hello World");
           return true;
       }
       catch (Exception e) {
           throw new DummyException("Something went wrong");
       }
   }

In my case, I was mocking this object to throw an IOException when the doSomething method was being called. However, because doSomething only throws a DummyException, this was triggering the error to be raised. So doing the mock method I posted up above doesn’t work.

But there’s a way to make it work.

Solution

The answer I found was to use something out of the MockitoBDD package, and use the given.willAnswer methods:

1
given(helper.doSomething()).willAnswer(invocation -> { throw new IOException();});

While this looks nearly identical to the when.thenThrow, this will actually allow you to return the specific Exception object when the defined event is hit. It will not care that the exception is not thrown on the called method. And for the sake of getting the scenarios covered, I was totally okay with this.

Here’s what the full test ended up looking like:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package net.fuzzylimes.blog;

import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import org.mockito.Mockito;

import java.io.IOException;

import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.when;

public class MainTest {

   Helper helper;

   @Before
   public void setUp() throws Exception {
       helper = Mockito.mock(Helper.class);
   }

   @Test(expected = DummyException.class)
   public void checkIfEqualGood() throws DummyException {
       given(helper.doSomething()).willAnswer(invocation -> { throw new IOException();});
       Main main = new Main();
       main.checkIfEqual("ABC", "abc", helper);
   }
}

Conclusion

Why did this start happening? My only assumption is that something must have changed in the Exceptions that we had created. If that’s not the case, then I truly don’t know why it was working pre merge and not post. I had confirmed all of the versions were the same, and I didn’t see any changes made to those files… but it’s the only thing that would have made sense.

Hopefully you found this useful and were able to learn something along with me. As always, the code for this post can be found in my gethub repo.

💚 A.B.L.