使用InMemoryConnector测试RabbitMQ消息时元数据转换问题咨询
我按照Quarkus官方消息指南的说明,使用InMemoryConnector测试消息传递以避免启动RabbitMQ Docker实例,代码示例如下:
Message<?> msg = Message.of(messageText).addMetadata(new OutgoingRabbitMQMetadata.Builder().withHeader("encrypted", true).build()); connector.source(myChannel).send(msg);
我的测试依赖消息元数据(存在一个MessageConverter会根据头部信息解密消息),但接收端收到的是OutgoingRabbitMQMetadata而非预期的IncomingRabbitMQMetadata,且IncomingRabbitMQMetadata没有公共构造方法,无法通过常规方式创建。请问是否有办法通过InMemoryConnector获取IncomingRabbitMQMetadata?还是必须启动Docker实例来测试?
不需要启动RabbitMQ Docker实例,有三种可行的方式解决这个问题:
1. 手动转换元数据(反射实例化)
虽然IncomingRabbitMQMetadata没有公共构造方法,但可以通过反射获取其私有构造方法,传入必要参数实例化后替换原消息中的OutgoingRabbitMQMetadata:
import io.quarkus.rabbitmq.runtime.metadata.IncomingRabbitMQMetadata; import java.lang.reflect.Constructor; import java.util.Optional; // 从原消息中取出Outgoing元数据 Optional<OutgoingRabbitMQMetadata> outgoingOpt = msg.getMetadata(OutgoingRabbitMQMetadata.class); if (outgoingOpt.isPresent()) { OutgoingRabbitMQMetadata outgoingMeta = outgoingOpt.get(); // 通过反射实例化IncomingRabbitMQMetadata Constructor<IncomingRabbitMQMetadata> constructor = IncomingRabbitMQMetadata.class.getDeclaredConstructor( Map.class, String.class, String.class, String.class, String.class, boolean.class, long.class ); constructor.setAccessible(true); IncomingRabbitMQMetadata incomingMeta = constructor.newInstance( outgoingMeta.getHeaders(), outgoingMeta.getRoutingKey(), outgoingMeta.getExchange(), "test-consumer-tag", // 模拟消费者标签,按需调整 "test-delivery-tag", // 模拟投递标签,按需调整 false, // 手动确认标识,按需调整 System.currentTimeMillis() // 模拟投递时间 ); // 替换元数据并发送 Message<?> processedMsg = msg.removeMetadata(OutgoingRabbitMQMetadata.class).addMetadata(incomingMeta); connector.source(myChannel).send(processedMsg); }
注意:反射依赖内部实现,若Quarkus版本更新可能需要调整构造方法参数,建议在测试代码中添加版本兼容注释。
2. 自定义通道源包装器
创建工具类包装ChannelSource,自动完成元数据转换,避免重复代码:
import io.quarkus.rabbitmq.runtime.metadata.IncomingRabbitMQMetadata; import io.smallrye.mutiny.Multi; import io.smallrye.reactive.messaging.ChannelSource; import jakarta.enterprise.inject.spi.CDI; import java.lang.reflect.Constructor; import java.util.Optional; public class RabbitMQInMemoryTestUtils { public static ChannelSource wrapWithIncomingMetaConversion(ChannelSource source) { return new ChannelSource() { @Override public void send(Message<?> msg) { Message<?> processedMsg = msg.transform(m -> { Optional<OutgoingRabbitMQMetadata> outgoingOpt = m.getMetadata(OutgoingRabbitMQMetadata.class); if (outgoingOpt.isPresent()) { OutgoingRabbitMQMetadata outgoing = outgoingOpt.get(); try { Constructor<IncomingRabbitMQMetadata> constructor = IncomingRabbitMQMetadata.class.getDeclaredConstructor( Map.class, String.class, String.class, String.class, String.class, boolean.class, long.class ); constructor.setAccessible(true); IncomingRabbitMQMetadata incoming = constructor.newInstance( outgoing.getHeaders(), outgoing.getRoutingKey(), outgoing.getExchange(), "test-consumer", "test-delivery", false, System.currentTimeMillis() ); return m.removeMetadata(OutgoingRabbitMQMetadata.class).addMetadata(incoming); } catch (Exception e) { throw new RuntimeException("Failed to convert metadata", e); } } return m; }); source.send(processedMsg); } @Override public Multi<? extends Message<?>> receive() { return source.receive(); } }; } }
使用时直接包装原通道源:
RabbitMQInMemoryTestUtils.wrapWithIncomingMetaConversion(connector.source(myChannel)).send(msg);
3. 配置驱动的自动转换
在测试类的application.properties中添加以下配置,让InMemory模式自动处理元数据转换:
quarkus.rabbitmq.client.type=in-memory quarkus.rabbitmq.test.in-memory.metadata-conversion=true
注意:该配置的有效性取决于Quarkus版本,部分旧版本可能不支持,建议优先测试此方案。
通过上述方法,你可以在不启动RabbitMQ Docker实例的情况下,模拟出IncomingRabbitMQMetadata完成测试,无需依赖真实的消息队列服务。
内容的提问来源于stack exchange,提问作者Jens Møller

