繁体   English   中英

运行多个 Spring Boot 测试时,@MockBean 在 JMS 侦听器中使用不同的实例

[英]@MockBean uses different instance in JMS listener when running multiple Spring Boot tests

我写了一个 Spring Boot 测试,它写入一个 JMS 队列,并期待通过 JMS 侦听器进行一些处理。 在侦听器中,我试图从 S3 读取一个对象。 AmazonS3类应替换为 MockBean。 在我的测试中,我设置了这样的模拟:

@SpringBootTest
public class MyTest {

    @Autowired
    MyJmsPublisher jmsPlublisher;
    @MockBean
    AmazonS3 amazonS3;

    @Test
    public void test() {
        final S3Object s3Object = mock(S3Object.class);
        when(s3Object.getObjectContent()).thenReturn(mock(S3ObjectInputStream.class));
        when(amazonS3.getObject(anyString(), anyString())).thenReturn(s3Object);
        
        jmsPlublisher.publishMessage("mymessage");
        Awaitility.await().untilAsserted(() -> {
            //wait for something here
        });
    }
}


@Component
@RequiredArgsConstructor
public class MyJmsPublisher {

    private final JmsTemplate jmsTemplate;

    public void publishMessage(String message) {
        jmsTemplate.convertAndSend("destination", message);
    }
}

@Component
@RequiredArgsConstructor
public class MyJmsListener {

    private final AmazonS3 amazonS3;

    @JmsListener(destination = "destination")
    public void onMessageReceived(String message) {
        final S3ObjectInputStream objectContent = amazonS3.getObject("a", "b").getObjectContent();
        // some logic here
    }
}

但问题是,当运行多个 Spring Boot 测试时MyJmsListener类包含一个与测试中创建的不同的模拟。 这是一个模拟,但例如getObjectContent()返回 null。 但是当我单独运行测试时,一切正常。

我尝试将AmazonS3 bean 注入MyJmsPublisher并在那里调用MyJmsPublisher方法并且它起作用了。 所以我怀疑,这是因为 JMS 侦听器在不同的线程中运行。

我找到了这个线程并将reset为所有可用选项,但没有任何区别。 我还尝试了这个 OP 对他们@Bean的方法,在那里我通过@Bean注释创建了一个模拟,如下所示:

@Configuration
public class MyConfig {
   @Bean
   @Primary
   public AmazonS3 amazonS3() {
      return Mockito.mock(AmazonS3.class);
   }
}

但这只是具有与上述相同的行为。

那么在使用不同的线程(例如使用@JMSListener时,您实际上可以使用@MockBean注释吗? 或者我错过了什么?

带有@JmsListener注释的方法的 Spring Bean 正在注入由辅助线程激活时从先前测试执行中泄漏的 bean。 一个实用的解决方法是将测试执行程序配置为对每个类使用一个隔离的 VM 以避免此问题。

对于Maven 执行,您可以通过设置reuseForks选项来配置maven-failsafe-pluginmaven-surefire-plugin reuseForks maven-surefire-plugin 例如:

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-failsafe-plugin</artifactId>
    <configuration>
        <reuseForks>false</reuseForks>
    </configuration>
</plugin>

您还可以在 JUnit IDE 工具中轻松地将Fork mode更改为Class以执行多个测试。 IntelliJ 示例:

IntelliJ JUnit Fork 模式

使用@DirtiesContext不起作用,不幸的是,我仍然找不到根本原因 - 我的预感是它可能与使用 ActiveMQ 代理的内存实例有关,该实例位于共享的 VM 实例中通过处决。

@JmsListener注释与@MockBean / @SpyBean结合使用时,我们遇到了类似的问题。 在我们的例子中,为每个测试类使用单独的目的地解决了这个问题:

@JmsListener(destination = "${my.mq.topic.name}")
void consume(TextMessage message){
...
}

@SpringBootTest
@TestPropertySource(properties = "my.mq.topic.name=UniqueTopicName"})
class MyConsumerIT{
...
}

据我了解,Spring 必须为每个主题/队列创建一个单独的 JMS 消费者。 此配置强制 Spring 为此类创建单独的 JMS 使用者并将其正确注入到 TestContext 中。 与没有此配置的情况相比,Spring 为所有测试类重用了曾经创建的 Consumer。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM