监听竞争消费者队列的多个测试应用程序上下文导致间歇性测试失败
Multiple test application contexts listening on competing consumer queue cause intermittent test failures
我有一个正在测试的 JMSInboundGateway,它侦听 Apache Artemis 队列(竞争消费者)。我的测试向 Artemis 服务器发送消息并模拟目标服务。如果调用模拟服务,那么我已验证 JmsInboundGateway 已正确设置。
流程如下所示:
Test Sender -> Artemis Queue -> JmsInboundGateway -> DirectChannel -> ServiceActivator -> Mock(Destination Service)
如果它是 JUnit 测试套件中唯一的测试 class 运行,那么该测试运行得非常顺利;但是,如果套件中还有其他测试 class,则测试失败。我已经追踪到,当测试失败时,Artemis 队列最多有三个消费者:我假设两个用于没有 Mocked 服务 bean 的 ApplicationContexts,另一个用于具有 mocked bean 的上下文。测试通过或失败取决于正确的上下文是否收到消息。
我尝试过的一件似乎有效的事情是在特定配置文件处于活动状态时选择性地注册 JmsInboundGateway,并且仅在消息传递测试(当然还有实时应用程序)中激活该配置文件。
// Declaring my inbound gateways with the profile requirement
@Profile("messaging")
@Bean
public JmsInboundGateway jmsInboundGateway(ConnectionFactory connFactory) { ... }
// Running my tests with
@RunWith(SpringRunner.class)
@SpringBootTest
@ActiveProfiles("messaging")
public class MessagingTest { ... }
这会导致消息窃取上下文从不侦听队列中的消息,并允许适当的上下文成为独占侦听器。这不是一个特别令人满意的解决方案,因为我可能有多个测试 classes 需要 "messaging" 配置文件并且我已经验证它们会踩到彼此的脚趾。
如果我将@DirtiesContext 注释添加到每个标有@ActiveProfiles("messaging") 的测试,那么当有多个消息传递测试时,这似乎确实可以解决问题。在测试套件执行期间,我只在 Artemis 队列中观察到一个消费者,并且我可以有多个测试 classes 并启用消息传递。
// The following appears to permit me to have multiple messaging enabled tests
@RunWith(SpringRunner.class)
@SpringBootTest
@ActiveProfiles("messaging")
@DirtiesContext
public class MessagingTest { ... }
这对我来说也显得笨拙,但这是迄今为止我最好的解决方案。是否有任何我缺少的测试助手 and/or 模式可以帮助我解决这个问题?
非常感谢!
有了 @DirtiesContext
你真的走对了路。
您在测试中的应用程序上下文之间共享 JMS 资源的问题 classes。这就是您真正与一个或另一个现有消费者发生冲突的方式,只是因为另一个 class 的整个应用程序上下文在执行期间被缓存,并且它可以访问 JMS 以及您当前的上下文。
这确实是我们几年前在 Spring 集成中为我们的 JMS 和 JDBC 测试解决类似问题的方式。我们还要求 Spring 测试框架开发人员将此作为默认功能来执行,但这听起来是一个重大的突破性变化,并且对于不共享资源的典型单元测试来说是不合理的。
从那以后,我们的方向是始终考虑我们的测试是否启动了一些后台线程或是否可以访问共享资源,例如embedded MongoDB、Hazelcast 或文件系统上的某个目录。因此,在这些情况下,我们肯定会使用 @DirtiesContext
并且对通过的测试非常满意。
我有一个正在测试的 JMSInboundGateway,它侦听 Apache Artemis 队列(竞争消费者)。我的测试向 Artemis 服务器发送消息并模拟目标服务。如果调用模拟服务,那么我已验证 JmsInboundGateway 已正确设置。
流程如下所示:
Test Sender -> Artemis Queue -> JmsInboundGateway -> DirectChannel -> ServiceActivator -> Mock(Destination Service)
如果它是 JUnit 测试套件中唯一的测试 class 运行,那么该测试运行得非常顺利;但是,如果套件中还有其他测试 class,则测试失败。我已经追踪到,当测试失败时,Artemis 队列最多有三个消费者:我假设两个用于没有 Mocked 服务 bean 的 ApplicationContexts,另一个用于具有 mocked bean 的上下文。测试通过或失败取决于正确的上下文是否收到消息。
我尝试过的一件似乎有效的事情是在特定配置文件处于活动状态时选择性地注册 JmsInboundGateway,并且仅在消息传递测试(当然还有实时应用程序)中激活该配置文件。
// Declaring my inbound gateways with the profile requirement
@Profile("messaging")
@Bean
public JmsInboundGateway jmsInboundGateway(ConnectionFactory connFactory) { ... }
// Running my tests with
@RunWith(SpringRunner.class)
@SpringBootTest
@ActiveProfiles("messaging")
public class MessagingTest { ... }
这会导致消息窃取上下文从不侦听队列中的消息,并允许适当的上下文成为独占侦听器。这不是一个特别令人满意的解决方案,因为我可能有多个测试 classes 需要 "messaging" 配置文件并且我已经验证它们会踩到彼此的脚趾。
如果我将@DirtiesContext 注释添加到每个标有@ActiveProfiles("messaging") 的测试,那么当有多个消息传递测试时,这似乎确实可以解决问题。在测试套件执行期间,我只在 Artemis 队列中观察到一个消费者,并且我可以有多个测试 classes 并启用消息传递。
// The following appears to permit me to have multiple messaging enabled tests
@RunWith(SpringRunner.class)
@SpringBootTest
@ActiveProfiles("messaging")
@DirtiesContext
public class MessagingTest { ... }
这对我来说也显得笨拙,但这是迄今为止我最好的解决方案。是否有任何我缺少的测试助手 and/or 模式可以帮助我解决这个问题?
非常感谢!
有了 @DirtiesContext
你真的走对了路。
您在测试中的应用程序上下文之间共享 JMS 资源的问题 classes。这就是您真正与一个或另一个现有消费者发生冲突的方式,只是因为另一个 class 的整个应用程序上下文在执行期间被缓存,并且它可以访问 JMS 以及您当前的上下文。
这确实是我们几年前在 Spring 集成中为我们的 JMS 和 JDBC 测试解决类似问题的方式。我们还要求 Spring 测试框架开发人员将此作为默认功能来执行,但这听起来是一个重大的突破性变化,并且对于不共享资源的典型单元测试来说是不合理的。
从那以后,我们的方向是始终考虑我们的测试是否启动了一些后台线程或是否可以访问共享资源,例如embedded MongoDB、Hazelcast 或文件系统上的某个目录。因此,在这些情况下,我们肯定会使用 @DirtiesContext
并且对通过的测试非常满意。