I'm new in Mockito and I'm facing an issue regarding a stubbing argument mismatch.
so far I thought this would works fine since in my implementation jmsTemplate is a depedency of jmsTemplateService and everything is injected via @Mock and @InjectMocks
and theoretically I'm mocking the execution of the invoked dependent method
convertAndSend(String destinationName, final Object message, final MessagePostProcessor postProcessor)
main part of test
Message message = Mockito.mock(Message.class);
MessagePostProcessor messagePostProcessor = (x) -> message;
doNothing()
.when(jmsTemplate)
.convertAndSend(queue, obj, messagePostProcessor);
jmsTemplateService.sendMessage(queue, obj);
but mockito throws a stubbing argument mismatch error which actually made me understand I'm not stubbing correctly.
- this invocation of 'convertAndSend' method:
jmsTemplate.convertAndSend(
"queueA",
Obj(),
com.example.JmsTemplateService$$Lambda$383/0x0000000800dd9440@5fb7183b
);
-> at com.example.JmsTemplateService.sendMessage(JmsTemplateService.java:64)
- has following stubbing(s) with different arguments:
1. jmsTemplate.convertAndSend(
"queueA",
Obj(),
com.example.JmsTemplateServiceTest$$Lambda$382/0x0000000800ddb360@74ad8d05
);
it seems the last parameter is the actual problem but I don't know how to capture it without mocking the jmsTemplate dependencies too.
this is the current implementation of the JmsTemplateService.sendMessage(String queue, Obj obj)
public void sendMessage(String queue, Object obj) {
try {
jmsTemplate.convertAndSend(queue, obj, message -> {
//do stuffs before sending the message
});
}catch (Exception e) {
//handle exception
}
any hint of what could be wrong in mocking the convertAndSend method?
Thank you very much for the help!
it seems the last parameter is the actual problem but I don't know how to capture it without mocking the jmsTemplate dependencies too.
Actually, you are mocking jmsTemplate
, otherwise it'd throw an exception because jmsTemplate
variable is not a mock.
doNothing()
.when(jmsTemplate) <- a mock is expected to be used here
.convertAndSend(queue, obj, messagePostProcessor);
You're right that the last parameter is the actual problem since different instances are being used.
The first instance is defined by your lambda in the test:
MessagePostProcessor messagePostProcessor = (x) -> message;
And the second one is defined with the other lambda when you perform the call to jmsTemplate.convertAndSend(..., ..., message -> {...})
Posibly you're getting that error because you're verifying that the jmsTemplate.convertAndSend(...)
was called with the params you defined in the tests.
Something like:
verify(jmsTemplate).convertAndSend(queue, obj, messagePostProcessor);
What you can do is expect any()
as a 3rd argument in that verify, and expect something like ArgumentsMatchers.eq(queue)
and ArgumentsMatchers.eq(obj)
as other arguments:
verify(jmsTemplate).convertAndSend(ArgumentsMatchers.eq(queue), ArgumentsMatchers.eq(obj), any());
class JmsTemplateServiceTest {
@Mock
private JmsTemplate jmsTemplate;
@Mock
private Message message;
@Captor
private ArgumentCaptor<String> queueCaptor;
@Captor
private ArgumentCaptor<String> messageCaptor;
@Captor
private ArgumentCaptor<MessagePostProcessor> messagePostProcessorArgumentCaptor;
@BeforeEach
void setUp() {
MockitoAnnotations.initMocks(this);
}
@Test
void testJmsTemplateWorksWithCorrectMessagePostProcessor() throws JMSException {
// Given
String destinationQueue = "test-queue";
String testMessage = "test message";
// When
doNothing().when(jmsTemplate).convertAndSend(destinationQueue, testMessage, message -> message);
// Inline dummy implementation that adds an element to a list defined in this test scope
List<String> stateCustomDependency = new ArrayList<>();
CustomDependency customDependency = () -> stateCustomDependency.add("Got executed!");
// Performs service call
JmsTemplateService jmsTemplateService = new JmsTemplateService(jmsTemplate, customDependency);
jmsTemplateService.sendMessage(destinationQueue, testMessage);
// Then verify the service actually invokes the jmsTemplate and capture the arguments passed to it
verify(jmsTemplate).convertAndSend(queueCaptor.capture(), messageCaptor.capture(), messagePostProcessorArgumentCaptor.capture());
// Verify values of queue and message
assertEquals(destinationQueue, queueCaptor.getValue());
assertEquals(testMessage, messageCaptor.getValue());
// Verify the MessagePostProcessor instantiated inside the service invokes the lambda dependencies
MessagePostProcessor messagePostProcessorToVerify = messagePostProcessorArgumentCaptor.getValue();
messagePostProcessorToVerify.postProcessMessage(message);
assertEquals(1, stateCustomDependency.size()); // Ensure the CustomDependency was invoked
assertEquals("Got executed!", stateCustomDependency.get(0)); // Ensure the right String was added by CustomDependency
}
}
In case you also need to call some method on Message object, you'll have to add statements like when(message.someMethod(...)).thenReturn(...)
.
I hope it helps!