【问题标题】:RabbitListenerTestHarness injects actual objects in listenersRabbitListenerTestHarness 在侦听器中注入实际对象
【发布时间】:2019-04-07 18:41:46
【问题描述】:

场景: Junit 用于微服务,它监听队列并在数据提取后发布到 rabbitMQ 中的交换。

问题:

RabbitListenerTestHarness 正在为 Rabbit 创建模拟对象 仅侦听器类,正在实例化实际对象 监听器自动装配组件

我找不到手动将模拟 bean 注入侦听器的方法。这会导致 Junit 在 Junit 执行期间将测试消息发布到微服务中配置的实际队列。

解决方法:我可以使用 rabbit-test 项目的唯一方法是配置测试交换以在 Junit 执行期间发布消息。

查询: 我想了解,是否有更好的方式为 Rabbit Listener 编写 Junit。我还想了解是否有办法将模拟对象注入到 Rabbit Listeners 自动装配组件中。

示例代码片段:

兔子监听类

@RabbitListener(id = "id", bindings = @QueueBinding(value = @Queue(value = "sampleQueue", durable = "true", autoDelete = "false"),key = "sampleRoutingKey", exchange = @Exchange(value = "sampleExchange", durable = "true", ignoreDeclarationExceptions = "true", type = EXCHANGE_TYPE)))
public void getMessageFromQueue(@Payload EventModel event) throws ListenerExecutionFailedException, JAXBException {
    dataExporterService.exportDataAndPostToRabbit(event);
}

服务类

@Autowired
DataExtractorRepository dataExtractorRepository;
@Autowired
DataPublihserRepository dataPublisherRepo;
public void exportDataAndPostToRabbit(EventModel event) throws JAXBException {
        dataPublisherRepo.sendMessageToExchange(dataExtractorRepository.extractOrderData(event), exchangeName, routingKeyValue);
}

DataPublihserRepository 具有内部自动装配的 rabbitTemplate。 DataExtractorRepository 在内部连接到 DB 以检索消息。

测试类

@Autowired
private RabbitListenerTestHarness harness;

@Autowired
private RabbitTemplate rabbitTemplate;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
DataExporterController = this.harness.getSpy("id");
}

@Test
public void shouldReceiveMessage() throws Exception {
LatchCountDownAndCallRealMethodAnswer answer = new LatchCountDownAndCallRealMethodAnswer(1);
doAnswer(answer).when(DataExporterController).getMessageFromQueue(any(EventModel.class));
rabbitTemplate.convertAndSend("sampleExchange", "sampleRoutingKey", createMessage());
assertTrue(answer.getLatch().await(10, TimeUnit.SECONDS));
verify(DataExporterController, times(1)).getMessageFromQueue(any(OrderEventsModel.class));
verify(orderDataExporterController, times(1)).getMessageFromQueue(any(OrderEventsModel.class));
}

 private Message createMessage() {
        String inputObject = "{\"id\":12345}";
        MessageProperties props = MessagePropertiesBuilder.newInstance().setContentType(MessageProperties.CONTENT_TYPE_JSON).build();
        return new Message(inputObject.getBytes(), props);
}

【问题讨论】:

    标签: java spring junit spring-amqp spring-rabbit


    【解决方案1】:

    该工具旨在作为一种机制来验证侦听器是否在集成测试中接收到数据。要对侦听器进行单元测试,请调用其 onMessage 方法。

    例如,使用 Mockito,给定

    public class MyListener {
    
        @Autowired
        private SomeService service;
    
        @RabbitListener(id = "myListener", queues = "foo")
        public void listen(Foo foo) {
            this.service.process(foo);
        }
    
    }
    

    public interface SomeService {
    
        void process(Foo foo);
    
    }
    

    然后

    @RunWith(SpringRunner.class)
    public class So53136882ApplicationTests {
    
        @Autowired
        private RabbitListenerEndpointRegistry registry;
    
        @Autowired
        private SomeService service;
    
        @Test
        public void test() throws Exception {
            SimpleMessageListenerContainer container = (SimpleMessageListenerContainer) this.registry
                    .getListenerContainer("myListener");
            ChannelAwareMessageListener listener = (ChannelAwareMessageListener) container.getMessageListener();
            Message message = MessageBuilder.withBody("{\"bar\":\"baz\"}".getBytes())
                    .andProperties(MessagePropertiesBuilder.newInstance()
                            .setContentType("application/json")
                            .build())
                    .build();
            listener.onMessage(message, mock(Channel.class));
            verify(this.service).process(new Foo("baz"));
        }
    
        @Configuration
        @EnableRabbit
        public static class config {
    
            @Bean
            public ConnectionFactory mockCf() {
                return mock(ConnectionFactory.class);
            }
    
            @Bean
            public MessageConverter converter() {
                return new Jackson2JsonMessageConverter();
            }
    
            @Bean
            public SimpleRabbitListenerContainerFactory rabbitListenerContainerFactory() {
                SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
                factory.setConnectionFactory(mockCf());
                factory.setMessageConverter(converter());
                factory.setAutoStartup(false);
                return factory;
            }
    
            @Bean
            public MyListener myListener() {
                return new MyListener();
            }
    
            @Bean
            public SomeService service() {
                return mock(SomeService.class);
            }
    
        }
    
    }
    

    请注意,容器工厂不会启动侦听器容器。

    为了测试发布,注入一个由RabbitTemplate实现的模拟RabbitOperations

    例如,给定

    public class SomeServiceImpl implements SomeService {
    
        @Autowired
        private RabbitOperations rabbitOperations;
    
        @Override
        public void process(Foo foo) {
            this.rabbitOperations.convertAndSend(
                    "someExchange", "someRoutingKey", new Foo(foo.getBar().toUpperCase()));
        }
    
    }
    

    @Bean
    public SomeService service() {
        return new SomeServiceImpl();
    }
    
    @Bean
    public RabbitOperations rabbitTemplate() {
        return mock(RabbitOperations.class);
    }
    

    然后

    @Test
    public void test() throws Exception {
        SimpleMessageListenerContainer container = (SimpleMessageListenerContainer) this.registry
                .getListenerContainer("myListener");
        ChannelAwareMessageListener listener = (ChannelAwareMessageListener) container.getMessageListener();
        Message message = MessageBuilder.withBody("{\"bar\":\"baz\"}".getBytes())
                .andProperties(MessagePropertiesBuilder.newInstance()
                        .setContentType("application/json")
                        .build())
                .build();
        listener.onMessage(message, mock(Channel.class));
        verify(this.rabbitTemplate).convertAndSend("someExchange", "someRoutingKey", new Foo("BAZ"));
    }
    

    【讨论】:

    • 嗨@Gary,首先非常感谢您提供有关如何为Rabbit Listener 实施单元测试用例的详细见解。它真的帮了我很多。此外,我试图在配置类的 SimpleRabbitListenerContainerFactory 组件中注入adviceChain 和错误处理程序,但这些类似乎没有被调用。是否需要一些配置?
    • 它们是侦听器容器的属性,并且由于我们直接(而不是通过容器)调用侦听器,因此它们被绕过了。 可以模拟 rabbitmq 客户端并在堆栈中的较低级别注入消息,但它非常复杂。见this test case for an example
    • 非常感谢。当然我会调查的。
    • @Jayadeep23 你也可以使用rabbitmq-mock 这样你就不会被嘲笑的东西打扰了,像错误处理程序这样的东西会在生产模式下被调用
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-10-07
    • 1970-01-01
    • 2019-04-23
    • 1970-01-01
    • 1970-01-01
    • 2012-05-05
    相关资源
    最近更新 更多