Reputation:
I am writing a unit test for a FizzConfigurator
class that looks like:
public class FizzConfigurator {
public void doFoo(String msg) {
doWidget(msg, Config.ALWAYS);
}
public void doBar(String msg) {
doWidget(msg, Config.NEVER);
}
public void doBuzz(String msg) {
doWidget(msg, Config.SOMETIMES);
}
public void doWidget(String msg, Config cfg) {
// Does a bunch of stuff and hits a database.
}
}
I'd like to write a simple unit test that stubs the doWidget(String,Config)
method (so that it doesn't actually fire and hit the database), but that allows me to verify that calling doBuzz(String)
ends up executing doWidget
. Mockito seems like the right tool for the job here.
public class FizzConfiguratorTest {
@Test
public void callingDoBuzzAlsoCallsDoWidget() {
FizzConfigurator fixture = Mockito.spy(new FizzConfigurator());
Mockito.when(fixture.doWidget(Mockito.anyString(), Config.ALWAYS)).
thenThrow(new RuntimeException());
try {
fixture.doBuzz("This should throw.");
// We should never get here. Calling doBuzz should invoke our
// stubbed doWidget, which throws an exception.
Assert.fail();
} catch(RuntimeException rte) {
return; // Test passed.
}
}
}
This seems like a good gameplan (to me at least). But when I actually go to code it up, I get the following compiler error on the 2nd line inside the test method (the Mockito.when(...)
line:
The method when(T) in the type Mockito is not applicable for the arguments (void)
I see that Mockito can't mock a method that returns void
. So I ask:
doBuzz
calls doWidget
under the hood? AnddoWidget
as it is the most critical method of my entire FizzConfigurator
class?Upvotes: 16
Views: 63059
Reputation: 3802
This isn't a direct answer to the question, but I ran across it when trying to troubleshoot my problem and haven't since found a more relevant question.
If you're trying to stub/mock an object marked as Spy, Mockito only picks up the stubs if they're created using the do...when
convention as hinted at by JB Nizet:
doReturn(Set.of(...)).when(mySpy).getSomething(...);
It wasn't being picked up by:
when(mySpy.getSomething(...)).thenReturn(Set.of(...));
Which matches the comment in MockHandlerImpl::handle
:
// stubbing voids with doThrow() or doAnswer() style
Upvotes: 2
Reputation: 2071
In my case, for the method I was trying to stub, I was passing in incorrect matchers.
My method signature (for the super class method I was trying to stub): String, Object.
I was passing in:
myMethod("string", Mockito.nullable(ClassType.class))
and getting:
Hints:
1. missing thenReturn()
2. you are trying to stub a final method, which is not supported
3: you are stubbing the behaviour of another mock inside before 'thenReturn' instruction if completed
When using a matcher in another parameter, we also need to use one for the string:
myMethod(eq("string"), Mockito.nullable(ClassType.class))
Hope this helps!
Upvotes: 0
Reputation: 306
This is a clear sign that doWidget
method should belong to another class which FizzConfigurator
would depend on.
In your test, this new dependency would be a mock, and you could easily verify if its method was called with verify
.
Upvotes: 0
Reputation: 692211
I wouldn't use exceptions to test that, but verifications. And another problem is that you can't use when()
with methods returning void.
Here's how I would do it:
FizzConfigurator fixture = Mockito.spy(new FizzConfigurator());
doNothing().when(fixture).doWidget(Mockito.anyString(), Mockito.<Config>any()));
fixture.doBuzz("some string");
Mockito.verify(fixture).doWidget("some string", Config.SOMETIMES);
Upvotes: 31