Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/spring/11.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Spring 为什么在其他测试类上需要DirtiesContext来模拟具有JMS侦听器的类的bean依赖关系_Spring_Spring Boot_Spring Test - Fatal编程技术网

Spring 为什么在其他测试类上需要DirtiesContext来模拟具有JMS侦听器的类的bean依赖关系

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上下文行为,我需要帮

上下文

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

观察到的测试行为

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

  • 模拟JMS使用者测试的bean依赖项需要早期的测试类具有DirtiesContext注释
  • 模拟RESTController的bean依赖关系似乎与JMS侦听器的工作方式不同,即在早期的测试类上不需要DirtiesContext

  • 我创建了一个简单的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。请添加任何错误和测试报告错误或断言失败是没有调用模拟。我正试图解释为什么没有模拟豆注射。