在 JmsTemplate 中模拟 MessegePostProcessor 接口

Mocking MessegePostProcessor Interface in JmsTemplate

我是 Mockito 的新手,我遇到了一个关于存根参数不匹配的问题。

到目前为止我认为这会很好,因为在我的实现中 jmsTemplatejmsTemplateService 的依赖项并且所有内容都是通过 @Mock 注入的和@InjectMocks

理论上我是在模拟被调用的依赖方法的执行

convertAndSend(String destinationName, final Object message, final MessagePostProcessor postProcessor)

测试的主要部分

Message message = Mockito.mock(Message.class);

MessagePostProcessor messagePostProcessor = (x) -> message;

doNothing()
           .when(jmsTemplate)
           .convertAndSend(queue, obj, messagePostProcessor);

jmsTemplateService.sendMessage(queue, obj);

但是 mockito 抛出一个存根参数不匹配错误,这实际上让我明白我存根不正确。

- this invocation of 'convertAndSend' method:
    jmsTemplate.convertAndSend(
    "queueA",
     Obj(),
  com.example.JmsTemplateService$$Lambda3/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$$Lambda2/0x0000000800ddb360@74ad8d05
);

最后一个参数似乎是实际问题,但我不知道如何在不模拟 jmsTemplate 依赖项的情况下捕获它。

这是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
    }

任何关于模拟 convertAndSend 方法可能出错的提示?

非常感谢您的帮助!

这是因为你用来存根JmsTemplateMessagePostProcessor与执行测试方法时传递给JmsTemplate的实际实例不同。

传递给模拟 JmsTemplateMessagePostProcessor 是在测试方法内部创建的新实例,而用于存根的实例是在测试方法外部创建的。它们显然是两个不同的实例。

因为 convertAndSend() in JmsTemplate return void ,你没有什么可以存根的。您可以在执行测试方法后简单地验证它是否使用预期的参数正确执行。喜欢的东西:

@ExtendWith(MockitoExtension.class)
public class JmsTemplateServiceTest {

    @Mock
    private JmsTemplate jmsTemplate;

    private JmsTemplateService jmsTemplateService;

    @BeforeEach
    public void init() {
        jmsTemplateService = new JmsTemplateService(jmsTemplate);
    }

    @Test
    public void convertAndSendTest() {
        Object someObject = ....;
        jmsTemplateService.sendMessage("queueA", someObject);
        verify(jmsTemplate).convertAndSend(eq("queueA"), eq(someObject), any());
    }

}

it seems the last parameter is the actual problem but I don't know how to capture it without mocking the jmsTemplate dependencies too.

实际上,您是在模拟 jmsTemplate,否则会抛出异常,因为 jmsTemplate 变量不是模拟。

doNothing()
       .when(jmsTemplate) <- a mock is expected to be used here
       .convertAndSend(queue, obj, messagePostProcessor);

你是对的,最后一个参数是实际问题,因为正在使用不同的实例。

第一个实例由您在测试中的 lambda 定义:

MessagePostProcessor messagePostProcessor = (x) -> message;

当您执行对 jmsTemplate.convertAndSend(..., ..., message -> {...})

的调用时,第二个是用另一个 lambda 定义的

您可能会收到该错误,因为您正在验证是否使用您在测试中定义的参数调用了 jmsTemplate.convertAndSend(...)。 类似于:

verify(jmsTemplate).convertAndSend(queue, obj, messagePostProcessor);

你可以做的是期望 any() 作为验证中的第三个参数,并期望像 ArgumentsMatchers.eq(queue)ArgumentsMatchers.eq(obj) 作为其他参数:

verify(jmsTemplate).convertAndSend(ArgumentsMatchers.eq(queue), ArgumentsMatchers.eq(obj), any());

更新:测试 MessagePostProcessor 内部依赖关系的可能解决方案

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
    }
}

如果您还需要在 Message 对象上调用某些方法,则必须添加 when(message.someMethod(...)).thenReturn(...).

之类的语句

希望对您有所帮助!