Reputation: 1
I was in a Java class and the teachers demonstrated a process like this:
Subscriber
class. The Subscriber
will consume Message Queue inputs and call a certain function in Service
class.Mockito.spy()
on the Subscriber
class and publish something into the Message Queue. Wait for 2 seconds, and verify if Service
method is actually called once.The Subscriber
has a function like this:
@Bean
public Consumer<InputVO> subscribeInput() {
return inputVO -> {
inputService.someMethod(inputVo);
};
}
The test, then, is basically like this:
@Autowired
InputSubscriber inputSubscriber;
@Autowired
StreamBridge streamBridge;
@MockBean
InputService inputService;
@Test
public void subscriberMethod_WillBeCalled_WhenInputReceived() throws InterruptedException {
InputSubscriber subscriberSpy = Mockito.spy(inputSubscriber);
doNothing().when(inputService).someMethod(any(InputVO.class));
InputVO expected = ...;
streamBridge.send("binding", expected);
TimeUnit.SECONDS.sleep(2);
verify(inputService, times(1)).someMethod(expected);
// will fail with unfinished mock error for below line.
// verify(subscriberSpy, times(1)).subscribeInput();
}
The above test then passed, but if we un-comment the last verify()
there will always be exception. The actual exception is like this:
org.mockito.exceptions.misusing.UnfinishedVerificationException:
Missing method call for verify(mock) here:
-> at
(Here it writes the name and position of test function.)
Example of correct verification:
verify(mock).doSomething()
I tried my best to keep only the mandatory parts, if it's still too long or falls lacking informations please let me know.
Does spying with a message queue work?
@SpringBootTest
and @Import(TestChannelBinderConfiguration.class)
. So the Subscriber
should be actually started right? Then when we publish the expected InputVo
to MQ, and Consumer in Subscriber
class consumes it, does the spy know? I googled around and seems like spy can only track behaviours directly called through it instead of the object being spied on.verify()
the spy always throw exception during the class.
verify()
on subscriberSpy
it always throw exception, and Mockito reports the reason as Unfinished Mock
which says it expects the callee function after verify()
. But we already wrote it as verify(subscriberSpy, times(1)).subscribeInput();
. The teachers said the reason might be that we mixed Spring MockBean and Spy together. (The spy is spying on an object that injected a @MockBean
(Service
).), but we never found out the actual reason behind this. I understand that this might be hard to answer because the codes are shortened, but a possible direction pointing on this issue is also welcome.Upvotes: 1
Views: 462
Reputation: 1802
The answer to your first question (and the title question) is straightforward - the spy you're creating in the test method is not involved in the actual code as it's not injected, passed anywhere etc. If you're using an IDE you can see that if the subscriberSpy
verification is commented out, the variable is marked as never used. To work around that you should use the @SpyBean
annotation over your inputSubscriber
field. Thanks to that the bean will be created and spied on before being injected into the Spring context and you will be able to use it in your test to verify interactions.
The second question is more tricky, because you did not show your full InputSubscriber
implementation. You call the verify
method on the spy correctly (even though the spy is incorrect - as described above), but the UnfinishedVerificationException
also says (the code can be found here):
Also, this error might show up because you verify either of: final/private/equals()/hashCode() methods.
Mocking methods declared on non-public parent classes is not supported.
So I'd assume that the InputSubscriber
bean injected into the test falls into one of the above "categories". Node: this could also conflict with the @SpyBean
annotation usage described above, but without the code it's hard to judge.
Upvotes: 1