使用Mockito和JUnit测试RabbitTemplate#convertAndS作为lambda发送

时间:2018-09-05 19:03:09

标签: java spring-boot junit rabbitmq mockito

我正在尝试测试写为lambda的RabbitTemplate#convertAndSend方法,就像这样:

// other stuff omitted for brevity

        rabbitTemplate.convertAndSend(myQueue, jsonString, message -> {
        message.getMessageProperties().setPriority(priority);
        return message;
        });

// other stuff omitted for brevity

我要尝试的测试用例是使用ArgumentCaptor的情况,以验证是否使用正确的参数调用了该方法。

@Test
public void givenMyNotification_whenElementIsSent_thenSetPriorityAndSendValidParameters() {

final ArgumentCaptor<String> captor = ArgumentCaptor.forClass(String.class);
final int expectedPriority = 5;
final Notification expected = TestUtils.getNotification();

testClass.handleNotification(expected);

verify(rabbitTemplate).convertAndSend(captor.capture(), captor.capture(),
    ArgumentMatchers.eq(MessagePostProcessor.class));

// assertThat...
));

}

由于参数不同,测试在验证步骤失败。

Wanted:
<Capturing argument>,
<Capturing argument>,
interface org.springframework.amqp.core.MessagePostProcessor

Actual invocation:
"myQueue",
"myJson",
com.example.notification.service.NotificationService$$Lambda$5/73698537@5bda80bf

我已经尝试过其他一些来自Matchito和hamcrest的Matchers,但无济于事。

所以,我的问题是:

  1. 一个人如何测试这种事情?

  2. 这是否是一种很好的做法,或者还有其他/更好的方法来测试兔子模板的发送吗?

2 个答案:

答案 0 :(得分:1)

除了最后一个匹配项ArgumentMatchers.eq(MessagePostProcessor.class)以外,您几乎都可以接受。

您实际上要求 Mockito 等同性匹配到该参数的类。 您应该匹配:

  • 使用ArgumentMatchers.any(MessagePostProcessor.class)的参数类型
  • 或实际值ArgumentMatchers.eq(expectedMessageProcessor)(如果您碰巧拥有的话)
  • 或参数捕获器(如果您确实需要检查参数的值)

但是在这种特殊情况下,如果使用第一个选项,则可能会遇到编译器问题,因为RabbitTemplate类具有两种类似的方法:

  • convertAndSend(字符串,字符串,对象)
  • convertAndSend(字符串,对象,MessagePostProcessor)

要解决此问题,您可以像这样将第二个参数的类型强制设置为对象:

Mockito.verify(rabbitTemplate).convertAndSend(captor.capture(), (Object) captor.capture(),
            Mockito.any(MessagePostProcessor.class));

或者更好的是,对于两个不同的参数,有两个不同的ArgumentCaptor

ArgumentCaptor<String> routingKeyCaptor = ArgumentCaptor.forClass(String.class);
ArgumentCaptor<Object> messageCaptor = ArgumentCaptor.forClass(Object.class);

...

verify(rabbitTemplate).convertAndSend(routingKeyCaptor.capture(), messageCaptor.capture(), any(MessagePostProcessor.class));

希望这会有所帮助!

答案 1 :(得分:0)

我认为这全都取决于您真正想要测试的东西以及它所带来的商业价值。

  1. 您要测试RabbitTemplate本身吗? Spring存储库已对此进行了测试,但是恕我直言,您不需要这样做。
  2. 您是否正在测试Spring Service,并且必须验证是否已发送消息?
  3. 您要确保邮件到达目的地吗?

对于(1),有一些示例说明了您正在Spring Framework here in their unit tests for the RabbitTemplate中编写的测试类型。

例如,验证基础ConnectionFactory实际上是created a channel

    txTemplate.execute(status -> {
        template.convertAndSend("foo", "bar");
        return null;
    });
    txTemplate.execute(status -> {
        template.convertAndSend("baz", "qux");
        return null;
    });
    verify(mockConnectionFactory, Mockito.times(1)).newConnection(any(ExecutorService.class), anyString());
    // ensure we used the same channel
    verify(mockConnection, times(1)).createChannel();

对于上面的(2),您不需要使用参数捕获器,等等。您可以将模拟RabbitTemplate注入到服务中,并且我个人希望针对使用或调用方法的参数进行测试使用Mockito#any

FYI,在断言方面,我还要看一下AssertJ,它得到了Spring的认可,它的流畅API(尤其是intellisense)使其变得非常容易使用。

对于(3),我强烈推荐https://www.testcontainers.org/(如果您使用的是docker),但我还没有检查出来,但是可能会有一个JUnit @Rule用于创建RabbitMQ经纪人(我必须对此进行检查)。我为ActiveMQ Artemis做过类似的事情,它很快为您启动了代理,您可以使用排队的消息。