You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Boot中@MockBean在RabbitMQ监听器的注入失效问题及解决办法

问题背景

我编写了一个集成测试基类,通过Testcontainer搭建RabbitMQ环境,所有集成测试类均继承该基类,运行情况良好。但其中一个测试需使用Mock实例作为Bean:单独运行该测试时一切正常,但若有其他集成测试先于它运行,Mock Bean无法注入到RabbitMQ监听器实例中,反而使用了Bean的实现类。

已尝试的方案
  • @DirtiesContext
    • 仅添加到该测试类无效
    • 同时添加到两个测试类有效
  • 结合@Primary注解的@TestConfiguration配置类
相关代码

RabbitMQ监听器代码

@Component
public class MessageConsumer {
  private final BeanToConsume bean;

  public MessageConsumer(BeanToConsume bean) {
    this.bean = bean;
  }

  @RabbitListener(bindings = {
      @QueueBinding(
          value = @Queue(value = "test", durable = "true"),
          exchange = @Exchange(value = "amq.fanout", type = "fanout")
      )
  })
  public void getBean() {
    System.out.println("BeanToConsume is of type: " + bean.getClass().getName());
  }
}

测试基类代码

@SpringBootTest
class AbstractIntegrationTest {

  static RabbitMQContainer rabbitMQ = new RabbitMQContainer("rabbitmq:3.9.5-alpine");

  static {
    rabbitMQ.start();
  }

  @DynamicPropertySource
  static void setRabbitMQ(DynamicPropertyRegistry registry) {
    registry.add("spring.rabbitmq.host", () -> "localhost");
    registry.add("spring.rabbitmq.port", () -> rabbitMQ.getAmqpPort());
    registry.add("spring.rabbitmq.username", () -> rabbitMQ.getAdminUsername());
    registry.add("spring.rabbitmq.password", () -> rabbitMQ.getAdminPassword());
  }
}

测试类代码

先运行的测试类

class FirstIntegrationTest extends AbstractIntegrationTest {
  @Autowired
  RabbitTemplate rabbitTemplate;

  @Test
  void test() {
    rabbitTemplate.convertAndSend("amq.fanout", "", "test");
  }
}

后运行的测试类

class SecondIntegrationTest extends AbstractIntegrationTest {
  @MockBean
  BeanToConsume beanToConsume;

  @Autowired
  RabbitTemplate rabbitTemplate;

  @Test
  void test() {
    rabbitTemplate.convertAndSend("amq.fanout", "", "test");
  }
}
可行解决方案

1. 调整@DirtiesContext的作用范围

给先运行的测试类添加@DirtiesContext(classMode = AFTER_CLASS),这样在它运行完毕后直接销毁上下文,避免后续测试复用已初始化的上下文(RabbitMQ监听器在上下文启动时就已创建,复用上下文不会重新实例化监听器,导致MockBean无法注入)。

2. 使用@RabbitListenerTest注解

Spring AMQP提供的@RabbitListenerTest可以专门针对RabbitMQ监听器做测试,它会重新初始化监听器相关组件,确保MockBean能正确注入。修改后运行的测试类:

@SpringBootTest
@RabbitListenerTest(MessageConsumer.class)
class SecondIntegrationTest extends AbstractIntegrationTest {
  @MockBean
  BeanToConsume beanToConsume;

  @Autowired
  RabbitTemplate rabbitTemplate;

  @Test
  void test() {
    rabbitTemplate.convertAndSend("amq.fanout", "", "test");
  }
}

3. 手动重启Rabbit监听器容器

在后运行的测试类的@BeforeEach方法中,重启所有Rabbit监听器容器,触发监听器重新实例化,从而获取已注册的MockBean:

class SecondIntegrationTest extends AbstractIntegrationTest {
  @MockBean
  BeanToConsume beanToConsume;

  @Autowired
  RabbitTemplate rabbitTemplate;
  
  @Autowired
  RabbitListenerEndpointRegistry rabbitListenerEndpointRegistry;

  @BeforeEach
  void restartListeners() {
    rabbitListenerEndpointRegistry.getListenerContainers().forEach(MessageListenerContainer::stop);
    rabbitListenerEndpointRegistry.getListenerContainers().forEach(MessageListenerContainer::start);
  }

  @Test
  void test() {
    rabbitTemplate.convertAndSend("amq.fanout", "", "test");
  }
}

4. 自定义TestConfiguration并刷新上下文

通过@TestConfiguration注册MockBean,再手动刷新上下文并重启监听器,确保监听器使用最新的Bean实例:

class SecondIntegrationTest extends AbstractIntegrationTest {
  @Autowired
  ConfigurableApplicationContext applicationContext;

  @TestConfiguration
  static class MockConfig {
    @Bean
    @Primary
    BeanToConsume beanToConsume() {
      return Mockito.mock(BeanToConsume.class);
    }
  }

  @Autowired
  RabbitTemplate rabbitTemplate;
  
  @Autowired
  RabbitListenerEndpointRegistry rabbitListenerEndpointRegistry;

  @BeforeEach
  void refreshAndRestart() {
    applicationContext.refresh();
    rabbitListenerEndpointRegistry.getListenerContainers().forEach(container -> {
      container.stop();
      container.start();
    });
  }

  @Test
  void test() {
    rabbitTemplate.convertAndSend("amq.fanout", "", "test");
  }
}

内容的提问来源于stack exchange,提问作者twobiers

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.05 00:20:30