Spring 为什么在其他测试类上需要DirtiesContext来模拟具有JMS侦听器的类的bean依赖关系
上下文 具有Rest端点和JMS AMQ侦听器的Spring引导应用程序 观察到的测试行为 测试类运行良好,无需单独使用DirtiesContext,但当运行整个测试类套件时,会观察到以下行为-Spring 为什么在其他测试类上需要DirtiesContext来模拟具有JMS侦听器的类的bean依赖关系,spring,spring-boot,spring-test,Spring,Spring Boot,Spring Test,上下文 具有Rest端点和JMS AMQ侦听器的Spring引导应用程序 观察到的测试行为 测试类运行良好,无需单独使用DirtiesContext,但当运行整个测试类套件时,会观察到以下行为- 模拟JMS使用者测试的bean依赖项需要早期的测试类具有DirtiesContext注释 模拟RESTController的bean依赖关系似乎与JMS侦听器的工作方式不同,即在早期的测试类上不需要DirtiesContext 我创建了一个简单的Spring应用程序来重现Spring上下文行为,我需要帮
我创建了一个简单的Spring应用程序来重现Spring上下文行为,我需要帮助理解-发生这种情况的原因是,如果没有
@DirtiesContext
,Spring将保留上下文以供共享相同设置的其他测试重用(请参阅中有关上下文缓存的更多信息)。这对于您的设置并不理想,因为您有一个消息侦听器,因为现在多个Spring上下文可以保持活动状态,并窃取您使用JmsTemplate
放入队列的消息
使用@DirtiesContext
可确保停止应用程序上下文,因此该上下文随后不活动,无法使用消息:
从@DirtiesContext:
测试注释,它指示{@link org.springframework.context.ApplicationContext ApplicationContext}*
与测试关联的是脏的,因此应
已关闭并从上下文缓存中删除
出于性能方面的考虑,我会尽量不太频繁地使用@DirtiesContext
,而是确保JMS目标对于测试期间启动的每个上下文都是唯一的。您可以通过将目标
值外包到配置文件(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);
}
}
公共类DestinationValueInitializer实现
ApplicationContextInitializer{
@凌驾
public void初始化(ConfigurableApplicationContext applicationContext){
TestPropertyValues.of(“consumer.destination=“+UUID.randomUUID().toString()).applyTo(applicationContext);
}
}
我为您的项目提供了一个示例,您可以在日志中看到这一点,即不同的应用程序上下文正在使用您的消息,因此您无法验证是否在编写测试的应用程序上下文中调用了存储库。如果没有
DirtiesContext
,会出现什么问题?请添加错误和测试报告在调试模式下运行整个测试套件时,我发现对于失败的JMS使用者测试,使用者类中存在实际的EnergiecGreeter bean,而不是预期的模拟bean。请添加任何错误和测试报告错误或断言失败是没有调用模拟。我正试图解释为什么没有模拟豆注射。