如何在 spring 启动测试中模拟 spring amqp/rabbit

how to mock spring amqp/rabbit in spring boot test

如何模拟 spring rabbitmq/amqp 以便在尝试自动创建 exchanges/queues 时在 Spring 引导测试期间不会失败?

鉴于我有一个简单的 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 启动测试中,像这样:

@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都mock掉呢?

这不是特别容易,如果代理不可用,我们通常使用 JUnit @Rule 跳过测试。

但是,我们确实有很多使用模拟的测试,但您确实必须了解很多 Spring AMQP 内部结构才能使用它们。您可以在 project itself.

中探索测试用例

有一次我确实尝试编写一个模拟代理,但最终工作量太大了。

我在某个时候有类似的要求,并查看了 QPid,它提供了一个内存中的 AMQP 代理。它迫使您停留在 AMQP 级别,并尽可能少地使用 rabbitMq 特定代码。

但我实际上找到了另一种方法:通过在 运行ning 测试 + auto-delete 值时调整队列和交换的名称,我们不再有问题了。测试中的所有 queue/exchange 名称都带有用户名(帐户 运行ning 测试)的后缀,这意味着每个人都可以 运行 在他们的机器上进行测试而不影响其他人。

即使在我们的 CI 管道中,多个项目也可能使用相同的 exchanges/queues :我们将测试中的值配置为特定于项目的,因此即使 2 个项目 运行 他们的同一用户在同一台​​机器上同时测试,消息不会 "leak" 当前测试之外。

与模拟或生成内存代理相比,这最终更易于管理。

在我们的项目中,我们在本地使用 docker 容器初始化一个 RabbitMQ 实例。对于 运行 集成测试,我们会在测试用例的开头启动一个 RabbitMQ 实例,并在清理期间将其关闭。

我们正在使用 TestContainers 来做到这一点。请参阅 https://www.testcontainers.org/usage/dockerfile.html and/or https://www.testcontainers.org/usage/docker_compose.html

不确定这是否有帮助,但我遇到了同样的问题。所以,我只是在 RabbitAdmin 上使用 @MockBean 和不同的配置文件,并没有遇到相同的连接问题。测试通过。

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

首先,在您的测试包中创建一个 @ConfigurationConnectionFactory

@Configuration
public class RabbitMqConfiguration {

    @Bean
    ConnectionFactory connectionFactory() {
        return new CachingConnectionFactory();
    }

    @Bean
    public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory) {
       return new RabbitTemplate(connectionFactory);
    }

}

之后,在您的 application.yml 测试包中设置此 属性:

spring:
  autoconfigure:
    exclude: org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration

这应该适用于 Spring Boot 2。2.x。

对于Spring Boot 1.5.x我还需要添加一个依赖:

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-stream-test-support</artifactId>
    <scope>test</scope>
</dependency>

我不知道为什么,但在没有 spring-cloud-stream-test-support 依赖项的情况下,我的集成测试尝试连接到 RabbitMQ 代理。即使不影响测试本身的结果,这也会在每次测试中偷走很多秒。我已经在 .

中看到了这种奇怪的行为

我知道这是一个老话题,但我想介绍一下我正在开发的模拟库:rabbitmq-mock

这个 mock 的目的是模仿没有 IO(不启动服务器、监听某些端口等)和较小的 (~ none) 启动时间的 RabbitMQ 行为。

在 Maven Central 中可用:

<dependency>
    <groupId>com.github.fridujo</groupId>
    <artifactId>rabbitmq-mock</artifactId>
    <version>1.1.1</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

希望对您有所帮助!

有点类似于 但对我不起作用:

这对我有用:

@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 {

}