为什么在其他测试类上需要 DirtiesContext 来模拟使用 JMS Listener 的类的 Bean 依赖关系



>上下文

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

观察到的测试行为

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

  1. 模拟 JMS Consumer 测试的 Bean 依赖关系需要早期的测试类具有 DirtiesContext 注释。
  2. 模拟 RestController 的 bean 依赖关系似乎与 JMS 侦听器不同,即在早期的测试类中不需要 DirtiesContext

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

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

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

从@DirtiesContext:

测试注释,指示 {@link org.springframework.context.ApplicationContext ApplicationContext} * 与测试相关联是的,因此应该是 关闭并从上下文缓存中删除。

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

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

@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);
}
}

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

最新更新