I am unit testing a Spring Boot micro-service. I want to use Mockito to throw an exception when a method is called in a certain process so that I can improve unit test coverage. The problem is that this method (let's call it doB()
) is called by an object that only exists in the method's local scope (B
), created by a static final factory object (A
).
I want to do something along the lines of:
doThrow(new RuntimeException()).when(mockedB).doB(anyString());
so that I can assert that fooResult
returns null
and therefore improve test coverage by covering the catch exception case.
But, is it even possible to create a mockedB
in a useful way? If so, how?
I have tried various combinations of mock()
and spy()
, admittedly with little success. I'm new to using Mockito, but I'm pretty sure the crux of the problem is because if I just mock Foo then I won't be able to see the A
and B
inside doing things, but trying to mock or spy A
or B
doesn't work either since they aren't the same as the A
or B
created inside of Foo
. A
being final and static probably isn't doing me any favors here either.
I have removed pretty much all but the bare essential functionality as an example. The class I am testing is Foo
which uses A
and B
internally.
Here is Foo
:
public class Foo {
private static final A localA = new A();
public FooResult doFoo(String fooString) {
try {
B localB = localA.createB();
return localB.doB(fooString);
} catch (RuntimeException e) {
//exception handling here
}
return null;
}
}
And this is A
:
public class A {
//unimportant internal details
private Object property;
public A() {
this(null);
}
public A(Object property) {
this.property = property;
}
public B createB() {
//assume for sake of example that this constructor
//is not easily accessible to classes other than A
return new B();
}
}
And now B
:
public class B {
public FooResult doB(String str) throws RuntimeException {
//lots of processing, yada yada...
//assume this exception is difficult to trigger just
//from input due to details out of our control
return new FooResult(str);
}
}
Here is FooResult
public class FooResult {
private String fooString;
public FooResult(String str) {
this.fooString = str;
}
public String getFooString() {
return fooString;
}
}
Finally, here is the test:
@RunWith(PowerMockRunner.class)
public class FooTest {
@InjectMocks
Foo foo;
@Test
public void testDoFoo() {
String fooString = "Hello Foo";
FooResult fooResult = foo.doFoo(fooString);
assertEquals(fooResult.getFooString(), fooString);
//works fine, nothing special here
}
@Test
@PrepareForTest({Foo.class, A.class, B.class})
public void testDoFooException() throws Exception {
//magic goes here???
A mockedA = PowerMockito.mock(A.class);
B mockedB = PowerMockito.mock(B.class);
PowerMockito.when(mockedA.createB()).thenReturn(mockedB);
PowerMockito.doThrow(new RuntimeException()).when(mockedB).doB(Mockito.anyString());
FooResult fooResult = foo.doFoo("Hello Foo");
//expect doFoo() to fail and return null
assertNull(fooResult);
}
}
As I said earlier, I expect the mock to trigger when doB()
is called, causing doB()
to return null
. This doesn't work and the exception is not thrown.
I have a feeling this is bad practice to be trying this. A better way would probably be to change the method so that I could pass in my own A
object instead so that I could observe it. But, let's just say that I can't change any source code. Is this even possible?