以前的集成测试使用当前的测试队列消息



所以我正在尝试在我的项目(rabbitmq(中使用一些队列。我决定创建简单的发布者/接收者集成测试。

所以我做了简单的发件人

@Component
public class QueueSender {
...
public void sendMessage(@RequestParam String message) {
rabbitTemplate.convertAndSend(queue, message);
}
}

和相应的测试

@SpringBootTest(classes = QueueSender.class, webEnvironment = SpringBootTest.WebEnvironment.NONE)
@Import(RabbitAutoConfiguration.class)
class QueueSenderTest {
... --docker rabbitmq instance is one for all tests.
@Test
void shouldSendMessageToQueue() {
String message = "hello world";
queueSender.sendMessage(message);
Object response = rabbitTemplate.receiveAndConvert(queue, MAX_TIMEOUT_MILLIS);
assertEquals(message, response);
}
}

和简单的接收器

@Component
public class QueueListener {
@RabbitListener(queuesToDeclare = @Queue("${queues.listener}"))
public void listener(Message object) {
System.out.println("received " + object);
}
}

和相应的测试

@SpringBootTest(classes = QueueListener.class, webEnvironment = SpringBootTest.WebEnvironment.NONE)
@Import(RabbitAutoConfiguration.class)
class QueueListenerTest {
... --docker rabbitmq instance is one for all tests.
@Test
void shouldFireListenerOnNewMessageOnQueue() {
String message = "hello world";
ArgumentCaptor<Message> argument = ArgumentCaptor.forClass(Message.class);
rabbitTemplate.convertAndSend(queue, message);
verify(queueListener, timeout(MAX_TIMEOUT_MILLIS)).listener(argument.capture());
assertEquals(message, new String(argument.getValue().getBody()));
}
}

队列配置放置在应用程序属性中

queues:
listener: "listen-queue-name"
sender: "sender-queue-name"

一切正常,直到我不会尝试对两个测试使用相同的队列并立即运行它们

queues:
listener: "listen-queue-name"
sender: "listen-queue-name"

在这种情况下,发件人测试总是失败。

因此,当我调试 SenderTest 时,似乎先前测试的 spring 上下文会消耗消息,因为调试器在 QueueListener 中停止,这甚至不应该在 QueueSender 测试-.-的上下文中

有趣的是,将其中一个测试类移动到另一个包可以解决问题,因此probalby SenderTest的触发速度如此之快,以至于先前测试中的RabbitListener仍然被注册。

DirtiesContext也可以工作,但我不想使用它。 有什么想法吗?

来自 Spring 文档

Spring 的测试框架在测试之间缓存应用程序上下文。 因此,只要您的测试共享相同的配置(否 不管它是如何被发现的(,潜在的耗时过程 加载上下文仅发生一次。

所以这就是你的问题。

不完全是你问题的答案,但更多的是一个建议。 您可以尝试使用测试容器 RabbitMQ.So 基本上您的测试用例会

1--启动RabbitMq测试容器,这将创建队列。

2--执行您的测试

3--销毁测试容器。

所有这些都应该很快。 这将确保所有测试类都使用独立的队列,因此没有 任何冲突的可能性。 在我看来,这应该是一种更干净的测试方法。

测试容器: https://www.testcontainers.org/modules/rabbitmq/

我想我有一个相关的问题。

设置

  • 集成测试,使用 rabbitmq 测试容器(共享,单例方法(
  • SpringBootTest, JUnit5
  • 测试类,每个
  • "场景"都有一个嵌套类,每个类都有几种测试方法
  • 测试发出事件,然后验证模块的事件处理程序是否完成其工作。我使用批准测试来断言生成的新的或更改的 JPA 实体的预期状态,由 MySQL 支持(也在测试容器中(
  • RabbitMQ 测试绑定是使用 application-test.yml 配置的;它导入主 application.yml,然后覆盖 cloud/stream Rabbit 和绑定,以便我的测试事件发送方发送的事件到达生产代码事件处理程序。

问题

  • 当只运行我的集成测试类时,例如从 IDE 运行,一切都很好。
  • 但是:当在同一 JVM 中运行模块的所有测试时 - 无论是从 IDE 还是通过 Maven,那么前面的集成测试似乎会干扰以后的运行。根据我的经验,每个事件处理程序仅在集成测试中第一次调用,当时其他集成测试类已经在同一 JVM 中运行。所有后续事件都不会到达其处理程序。

什么帮助

  • 重写绑定名称,使其在模块的测试中是唯一的。这可确保受测处理程序接收事件,即使某些幽灵 (?( 仍在侦听同一通道

  • 这可以通过各种方式完成(例如通过配置文件覆盖(,但最简单的方法是:将覆盖属性添加到类 SpringBootTest 注释中,例如:

    @SpringBootTest(properties = "spring.cloud.stream.bindings.myEventHandler-in-0.group=myUnique123EventTestGroup-v1")
    public class MyEventConsumersIntegrationTest {
    

仅供参考,我的应用程序测试.yml 中的绑定部分如下所示:

### In all testing:
spring:
config:
# application-test.yml is read INSTEAD of application.yml, hence need to import the latter explicitly:
import: classpath:application.yml
---
### Overrides for my module:
spring:
config:
activate:
on-profile: mymodule
cloud:
stream:
function:
definition: myEventHandler
rabbit:
bindings:
myEventHandler-in-0:
bindings:
myTestEventSender-out-0:
destination: myEventChannel
binder: rabbit
myEventHandler-in-0:
destination: myEventChannel
group: myEventGroup-v1       # Override the group value for each integration test class

最新更新