【问题标题】:how to mock spring amqp/rabbit in spring boot test如何在 Spring Boot 测试中模拟 Spring amqp/rabbit
【发布时间】:2016-12-09 08:21:10
【问题描述】:

如何模拟 spring rabbitmq/amqp,使其在 Spring Boot 测试期间尝试自动创建交换/队列时不会失败?

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

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

【问题讨论】:

    标签: spring spring-test spring-amqp spring-rabbit


    【解决方案1】:

    不确定这是否有帮助,但我遇到了同样的问题。所以,我只是在 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
    }
    

    【讨论】:

      【解决方案2】:

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

      这个模拟的目的是在没有 IO(不启动服务器、监听某个端口等)和少量(~无)启动时间的情况下模拟 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

      希望这能有所帮助!

      【讨论】:

      • 我试过了,一定是做错了什么。我得到:java.lang.ClassNotFoundException: com.rabbitmq.client.QueueingConsumer
      • rabbitmq-mock 代码不使用这个com.rabbitmq.client.QueueingConsumer,你用的是什么版本的amqp-client
      • 4.2.0.大约一年前,另一个开发人员实现了它。我只是想让测试运行而不会抛出异常。
      • @disco.dan.silver 你的依赖管理一定有问题,因为我只能找到 spring-rabbit (1.17.10.RELEASE) 指向 amqp-client (4.0.3) 和 spring-rabbit (2.0.0.RELEASE) 指向 amqp-client (5.0.0),没有提到 4.2.0 版本。除此之外,您链接的错误是由于缺少 deprecatedcom.rabbitmq.client.QueueingConsumer,它存在于 4.2.0 中,但在 5.0.0。您能否检查您的类路径中是否存在不需要的 ampq-client 依赖版本?
      • 我能找到的唯一拥有 amqp-client 的是 org.springframework.boot:spring-boot-starter-amqp。
      【解决方案3】:

      首先,在您的测试包中创建一个带有ConnectionFactory@Configuration

      @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 代理。即使不影响测试本身的结果,这在每次测试中都偷走了很多秒。我已经在another post 中看到了这种奇怪的行为。

      【讨论】:

        【解决方案4】:

        有点类似于 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】:

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

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

          【讨论】:

            【解决方案6】:

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

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

            即使在我们的 CI 管道中,多个项目也可能使用相同的交换/队列:我们将测试中的值配置为特定于项目,这样即使 2 个项目在同一台机器上同时运行它们的测试用户,消息不会在当前测试之外“泄漏”。

            这最终比模拟或生成内存代理要简单得多。

            【讨论】:

              【解决方案7】:

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

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

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

              【讨论】:

              • 很奇怪,它曾经与 1.5.6 一起使用 - 我只需要像这样配置一个 Bean:Mockito.mock(AmqpTemplate.class) - 但现在使用 1.6.1 这不再适用:(
              • 如果我是正确的,这也意味着当我有这样的配置时,我将无法使用任何带有完整容器的 SpringBoot 测试用例:(
              • 模拟模板很容易;它涉及更多的模拟经纪人响应(确认,退货,交付)。解释“不再起作用”——AmqpTemplate 是一个简单的interface; 1.6 中没有任何东西会改变模拟它的能力。对于@RabbitListener,您必须模拟一个侦听器容器。
              • 我不需要任何与 rabbit/amqp 相关的东西就可以在这个测试用例中工作,我想要的只是启动不会因为上面显示的错误而失败。
              • 对于 1.5.6,我只需要模拟 AmqpTemplate,然后我就可以启动嵌入式 tomcat,而不会出现任何错误。
              猜你喜欢
              • 2019-01-30
              • 1970-01-01
              • 2014-04-25
              • 2021-05-28
              • 1970-01-01
              • 2018-05-01
              • 1970-01-01
              • 1970-01-01
              • 2019-09-28
              相关资源
              最近更新 更多