如何模拟spring rabbitmq / amqp,以便在尝试自动创建交换/队列时在Spring Boot Test期间不会失败?
鉴于我有一个简单的RabbitListener
,会导致队列和交换自动创建如下:
@Component
@RabbitListener(bindings = {
@QueueBinding(
value = @Queue(value = "myqueue", autoDelete = "true"),
exchange = @Exchange(value = "myexchange", autoDelete = "true", type = "direct"),
key = "mykey")}
)
@RabbitListenerCondition
public class EventHandler {
@RabbitHandler
public void onEvent(Event event) {
...
}
}
在简单的Spring Boot Test中,像这样:
@ActiveProfiles("test")
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT, classes = { Application.class })
@Autowired
private ApplicationContext applicationContext;
@Test
public void test() {
assertNotNull(applicationContext);
}
}
它将失败:
16:22:16.527 [SimpleAsyncTaskExecutor-1] ERROR o.s.a.r.l.SimpleMessageListenerContainer - Failed to check/redeclare auto-delete queue(s).
org.springframework.amqp.AmqpConnectException: java.net.ConnectException: Connection refused
at org.springframework.amqp.rabbit.support.RabbitExceptionTranslator.convertRabbitAccessException(RabbitExceptionTranslator.java:62)
at org.springframework.amqp.rabbit.connection.AbstractConnectionFactory.createBareConnection(AbstractConnectionFactory.java:309)
在这个测试中,我不关心Rabbit / AMQP,那么我怎么能嘲笑整个Rabbit / AMQP呢?
答案 0 :(得分:8)
我知道这是一个古老的话题,但是我想介绍一个我正在开发的模拟库:rabbitmq-mock。
此模拟的目的是模拟没有IO(无需启动服务器,不侦听某些端口等)且启动时间短(〜无)的RabbitMQ行为。
它在Maven Central中可用:
<dependency>
<groupId>com.github.fridujo</groupId>
<artifactId>rabbitmq-mock</artifactId>
<version>1.0.10</version>
<scope>test</scope>
</dependency>
基本用途是使用测试项覆盖Spring配置:
@Configuration
@Import(AmqpApplication.class)
class AmqpApplicationTestConfiguration {
@Bean
public ConnectionFactory connectionFactory() {
return new CachingConnectionFactory(MockConnectionFactoryFactory.build());
}
}
要自动模拟Spring bean进行测试,请看一下我正在从事的另一个项目:spring-automocker
希望这会有所帮助!
答案 1 :(得分:5)
这并不是特别容易,如果经纪人不可用,我们通常会使用JUnit @Rule
来跳过测试。
但是,我们确实有很多使用模拟的测试,但你真的必须了解很多Spring AMQP内部使用它们。您可以在project itself。
中浏览测试用例有一次,我确实尝试过写一个模拟经纪人,但结果是工作太多了。
答案 2 :(得分:4)
不确定这是否有用,但我遇到了同样的问题。所以,我只是在@MockBean
上使用RabbitAdmin
使用不同的配置文件,我没有得到相同的连接问题。测试通过。
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE)
@RunWith(SpringRunner.class)
@ActiveProfiles("my-test")
public class ServiceTests {
@Autowired
private DummyService unitUnderTest;
@MockBean
private RabbitAdmin rabbitAdmin;
// lots of tests which do not need Spring to Create a RabbitAdmin Bean
}
答案 3 :(得分:2)
在我们的项目中,我们在本地使用RabbitMQ
容器初始化docker
实例。要运行集成测试,我们会在测试用例开始时启动RabbitMQ
实例,并在清理期间将其关闭。
我们正在使用TestContainers来做到这一点。请参阅https://www.testcontainers.org/usage/dockerfile.html和/或https://www.testcontainers.org/usage/docker_compose.html。
答案 4 :(得分:2)
类似于Rajkishan's answer的东西对我不起作用:
这反而对我有用:
@SpringBootApplication
public class MyTestsApp {
@Bean
@Primary
public CachingConnectionFactory rabbitAdmin() {
return Mockito.mock(CachingConnectionFactory.class);
}
}
@RunWith(SpringRunner.class)
@SpringBootTest(classes = {MyTestsApp.class})
@ActiveProfiles(profiles = "test")
public class MyTests {
}
答案 5 :(得分:1)
我在某个时候遇到了类似的要求,并研究了QPid,它提供了一个内存AMQP代理。它迫使你保持在AMQP级别,并使用尽可能少的rabbitMq特定代码。
但实际上我找到了另一种方法:通过在运行测试时调整队列和交换的名称+自动删除值,我们不再有问题了。测试中的所有队列/交换名称都以用户名(运行测试的帐户)为后缀,这意味着每个人都可以在其计算机上运行测试而不会影响其他人。
即使在我们的CI管道中,多个项目也可能使用相同的交换/队列:我们将测试中的值配置为项目特定的,这样即使两个项目在同一台机器上同时运行它们的测试也是如此用户,消息不会在当前测试之外“泄漏”。
这最终比管理或生成内存代理更容易管理。