[英]@MockBean uses different instance in JMS listener when running multiple Spring Boot tests
I've written a Spring Boot Test, that writes into a JMS queue and is expecting some processing via an JMS listener.我写了一个 Spring Boot 测试,它写入一个 JMS 队列,并期待通过 JMS 侦听器进行一些处理。 In the listener, I'm trying to read an object from S3.
在侦听器中,我试图从 S3 读取一个对象。 The
AmazonS3
class should be replaced by a MockBean. AmazonS3
类应替换为 MockBean。 In my test I set up the mock like this:在我的测试中,我设置了这样的模拟:
@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
}
}
But the issue is that when running multiple Spring Boot tests , the MyJmsListener
class contains a mock that is different from the one created in the Test.但问题是,当运行多个 Spring Boot 测试时,
MyJmsListener
类包含一个与测试中创建的不同的模拟。 It's a mock, but for example the getObjectContent()
returns null.这是一个模拟,但例如
getObjectContent()
返回 null。 But when I run the test alone, everything works fine.但是当我单独运行测试时,一切正常。
I've tried to inject the AmazonS3
bean into the MyJmsPublisher
and call the mocked method there and it worked.我尝试将
AmazonS3
bean 注入MyJmsPublisher
并在那里调用MyJmsPublisher
方法并且它起作用了。 So I suspect, that it's because the JMS listener operates in a different thread.所以我怀疑,这是因为 JMS 侦听器在不同的线程中运行。
I've found this thread and also set the reset
to all available options, but does not make any difference.我找到了这个线程并将
reset
为所有可用选项,但没有任何区别。 I also tried this OP's approach that worked for them, where I create a mock via the @Bean
annotation like this:我还尝试了这个 OP 对他们
@Bean
的方法,在那里我通过@Bean
注释创建了一个模拟,如下所示:
@Configuration
public class MyConfig {
@Bean
@Primary
public AmazonS3 amazonS3() {
return Mockito.mock(AmazonS3.class);
}
}
But this just has the same behavior as mentioned above.但这只是具有与上述相同的行为。
So can you actually use the @MockBean
annotation when using different Threads like using a @JMSListener
?那么在使用不同的线程(例如使用
@JMSListener
时,您实际上可以使用@MockBean
注释吗? Or am I missing something?或者我错过了什么?
Spring Beans with methods annotated with @JmsListener
are injecting beans leaked from previous test executions when activated by a secondary thread.带有
@JmsListener
注释的方法的 Spring Bean 正在注入由辅助线程激活时从先前测试执行中泄漏的 bean。 A practical workaround is to configure the test executor to use an isolated VM for each class to avoid this issue.一个实用的解决方法是将测试执行程序配置为对每个类使用一个隔离的 VM 以避免此问题。
For Maven executions you can configure the maven-failsafe-plugin
or maven-surefire-plugin
by setting the reuseForks
option.对于Maven 执行,您可以通过设置
reuseForks
选项来配置maven-failsafe-plugin
或maven-surefire-plugin
reuseForks
maven-surefire-plugin
。 eg:例如:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-failsafe-plugin</artifactId>
<configuration>
<reuseForks>false</reuseForks>
</configuration>
</plugin>
You can also easily change the Fork mode
to Class
in JUnit IDE tools for multiple tests execution.您还可以在 JUnit IDE 工具中轻松地将
Fork mode
更改为Class
以执行多个测试。 Example for IntelliJ: IntelliJ 示例:
Using @DirtiesContext
does not work, and unfortunately, I still couldn't find the root cause for this - My hunch is that it could be something related to using an in-memory instance of the ActiveMQ broker, which lives in the VM instance shared by the executions.使用
@DirtiesContext
不起作用,不幸的是,我仍然找不到根本原因 - 我的预感是它可能与使用 ActiveMQ 代理的内存实例有关,该实例位于共享的 VM 实例中通过处决。
We had a similar issue when using the @JmsListener
annotation in combination with @MockBean
/ @SpyBean
.将
@JmsListener
注释与@MockBean
/ @SpyBean
结合使用时,我们遇到了类似的问题。 In our case using a separate destination for each test class solved this problem:在我们的例子中,为每个测试类使用单独的目的地解决了这个问题:
@JmsListener(destination = "${my.mq.topic.name}")
void consume(TextMessage message){
...
}
@SpringBootTest
@TestPropertySource(properties = "my.mq.topic.name=UniqueTopicName"})
class MyConsumerIT{
...
}
As far as I understand Spring has to create a separate JMS Consumer for each topic/queue.据我了解,Spring 必须为每个主题/队列创建一个单独的 JMS 消费者。 This configuration forces Spring to create a separate JMS Consumer for this class and Inject it correctly into the TestContext.
此配置强制 Spring 为此类创建单独的 JMS 使用者并将其正确注入到 TestContext 中。 In comparison without this configuration, Spring reuses the once created Consumer for all test classes.
与没有此配置的情况相比,Spring 为所有测试类重用了曾经创建的 Consumer。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.