gpt4 book ai didi

spring - 为什么其他测试类需要 DirtiesContext 来模拟带有 JMS Listener 的类的 bean 依赖性

转载 作者:行者123 更新时间:2023-12-05 02:06:45 24 4
gpt4 key购买 nike

上下文

带有 Rest 端点和 JMS AMQ 监听器的 Spring Boot 应用程序

观察到的测试行为

测试类在不需要单独的 DirtiesContext 的情况下运行良好,但是当整个测试类套件运行时,会观察到以下行为 -

  1. 模拟 JMS 消费者测试的 bean 依赖项需要早期的测试类具有 DirtiesContext 注释。
  2. 模拟 RestController 的 bean 依赖性似乎与 JMS Listener 的工作方式不同,即在早期的测试类上不需要 DirtiesContext

我创建了一个简单的 Spring 应用程序来重现我需要帮助理解的 Spring 上下文行为 - https://github.com/ajaydivakaran/spring-dirties-context

最佳答案

发生这种情况的原因是没有 @DirtiesContext Spring 将保留上下文以供共享相同设置的其他测试重用(阅读 Spring documentation 中有关上下文缓存的更多信息) .这对于您的设置来说并不理想,因为您有一个消息监听器,因为现在多个 Spring 上下文可以保持事件状态并窃取您使用 JmsTemplate 放入队列中的消息。

使用 @DirtiesContext 确保停止应用程序上下文,因此此上下文之后不活动并且不能使用消息:

from @DirtiesContext:

Test annotation which indicates that the {@link org.springframework.context.ApplicationContext ApplicationContext} *associated with a test is dirty and should therefore beclosed and removed from the context cache.

出于性能方面的原因,我尽量不要过于频繁地使用 @DirtiesContext,而是确保 JMS 目标对于您在测试期间启动的每个上下文都是唯一的。您可以通过将 destination 值外包给配置文件 (application.properties) 并随机填充此值来实现此目的,例如使用 ContextInitializer .

第一个(简单的)实现可能如下所示:

@AllArgsConstructor
@Service
public class Consumer {
private EnergeticGreeter greeter;
private MessageRepository repository;
private ApplicationContext applicationContext;

@JmsListener(destination = "${consumer.destination}")
public void consume(
@Header(name = JmsHeaders.MESSAGE_ID, required = false) String messageId,
TextMessage textMessage) {

System.out.println("--- Consumed by context: " + applicationContext.toString());

if ("Ahem hello!!".equals(greeter.welcome().getContent())) {
repository.save();
}
}
}

对应的测试:

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@ContextConfiguration(initializers = DestinationValueInitializer.class)
public class JMSConsumerIntegrationTest {

@Autowired
private JmsTemplate jmsTemplate;

@Value("${consumer.destination}")
private String destination;

@Autowired
private ApplicationContext applicationContext;

@MockBean
private EnergeticGreeter greeter;

@MockBean
private MessageRepository repository;

//Todo - To get all tests in this project to pass when entire test suite is run look at Todos added.
@Test
public void shouldInvokeRepositoryWhenGreetedWithASpecificMessage() {
when(greeter.welcome()).thenReturn(new Message("Ahem hello!!"));

System.out.println("--- Send from context: " + applicationContext.toString());

jmsTemplate.send(destination, session -> session.createTextMessage("hello world"));

Awaitility.await().atMost(10, TimeUnit.SECONDS).untilAsserted(
() -> verify(repository, times(1)).save()
);
}
}

和上下文初始化器:

public class DestinationValueInitializer implements
ApplicationContextInitializer<ConfigurableApplicationContext> {

@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
TestPropertyValues.of("consumer.destination=" + UUID.randomUUID().toString()).applyTo(applicationContext);
}
}

我提供了一个 small PR对于您的项目,您可以在日志中看到这一点,不同的应用程序上下文正在使用您的消息,因此您无法验证存储库是否在您编写测试的应用程序上下文中被调用。

关于spring - 为什么其他测试类需要 DirtiesContext 来模拟带有 JMS Listener 的类的 bean 依赖性,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62489682/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com