如何在 Spring Boot 测试中模拟 spring amqp/rabbit
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/38746522/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
how to mock spring amqp/rabbit in spring boot test
提问by domi
How to mock spring rabbitmq/amqp so it will not fail during a Spring Boot Test while trying to auto create exchanges/queues?
如何模拟 spring rabbitmq/amqp 使其在 Spring Boot 测试期间不会在尝试自动创建交换/队列时失败?
Given I have a simple RabbitListener
that will cause the queue and exchange to be auto created like this:
鉴于我有一个简单的方法RabbitListener
,它将导致队列和交换被自动创建,如下所示:
@Component
@RabbitListener(bindings = {
@QueueBinding(
value = @Queue(value = "myqueue", autoDelete = "true"),
exchange = @Exchange(value = "myexchange", autoDelete = "true", type = "direct"),
key = "mykey")}
)
@RabbitListenerCondition
public class EventHandler {
@RabbitHandler
public void onEvent(Event event) {
...
}
}
During a simple Spring Boot Test, like this:
在一个简单的 Spring Boot 测试中,像这样:
@ActiveProfiles("test")
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT, classes = { Application.class })
@Autowired
private ApplicationContext applicationContext;
@Test
public void test() {
assertNotNull(applicationContext);
}
}
it will fail with:
它会失败:
16:22:16.527 [SimpleAsyncTaskExecutor-1] ERROR o.s.a.r.l.SimpleMessageListenerContainer - Failed to check/redeclare auto-delete queue(s).
org.springframework.amqp.AmqpConnectException: java.net.ConnectException: Connection refused
at org.springframework.amqp.rabbit.support.RabbitExceptionTranslator.convertRabbitAccessException(RabbitExceptionTranslator.java:62)
at org.springframework.amqp.rabbit.connection.AbstractConnectionFactory.createBareConnection(AbstractConnectionFactory.java:309)
In in this test I don't care about Rabbit/AMQP, so how can I mock the whole Rabbit/AMQP away?
在这个测试中,我不关心 Rabbit/AMQP,那么我如何模拟整个 Rabbit/AMQP 呢?
采纳答案by Gary Russell
It's not particularly easy, we generally use a JUnit @Rule
to skip the test if the broker's not available.
这不是特别容易,@Rule
如果代理不可用,我们通常使用 JUnit跳过测试。
However, we do have a lot of tests that use mocks, but you really have to understand a lot of the Spring AMQP internals to use them. You can explore the test cases in the project itself.
但是,我们确实有很多使用模拟的测试,但是您确实必须了解很多 Spring AMQP 内部结构才能使用它们。您可以探索项目本身中的测试用例。
At one point I did attempt writing a mock broker but it ended up being too much work.
有一次我确实尝试编写一个模拟经纪人,但最终工作量太大。
回答by Lo?c Le Doyen
I know this is an old topic, but I'd like to introduce a mocking library I'm developping : rabbitmq-mock.
我知道这是一个老话题,但我想介绍一个我正在开发的模拟库:rabbitmq-mock。
The purpose of this mock is to mimic RabbitMQ behavior without IO (without starting a server, listening to some port, etc.) and with minor (~ none) startup time.
这个模拟的目的是在没有 IO(没有启动服务器、监听某个端口等)和有少量(~无)启动时间的情况下模拟 RabbitMQ 行为。
It is available in Maven Central:
它在 Maven 中心可用:
<dependency>
<groupId>com.github.fridujo</groupId>
<artifactId>rabbitmq-mock</artifactId>
<version>1.0.14</version>
<scope>test</scope>
</dependency>
Basic use will be to override Spring configuration with a test one :
基本用途是使用测试覆盖 Spring 配置:
@Configuration
@Import(AmqpApplication.class)
class AmqpApplicationTestConfiguration {
@Bean
public ConnectionFactory connectionFactory() {
return new CachingConnectionFactory(MockConnectionFactoryFactory.build());
}
}
For automatic mocking of Spring beans for tests, give a look at another project I'm working on: spring-automocker
要自动模拟 Spring bean 进行测试,请查看我正在从事的另一个项目:spring-automocker
Hope this can help !
希望这可以帮助!
回答by Rajkishan Swami
Not sure if this is helpful but, i was having the same problem. So, i just used @MockBean
on RabbitAdmin
with a different profile, and did not get the same connection issues. Tests Passed.
不确定这是否有帮助,但是我遇到了同样的问题。所以,我只是用@MockBean
在RabbitAdmin
了不同的配置文件,并没有得到相同的连接问题。测试通过。
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE)
@RunWith(SpringRunner.class)
@ActiveProfiles("my-test")
public class ServiceTests {
@Autowired
private DummyService unitUnderTest;
@MockBean
private RabbitAdmin rabbitAdmin;
// lots of tests which do not need Spring to Create a RabbitAdmin Bean
}
回答by joy
In our project, we initialize a RabbitMQ
instance using a docker
container locally. To run an integration test, we do spin up a RabbitMQ
instance at the beginning of the test case and shut it down during clean-up.
在我们的项目中,我们在本地RabbitMQ
使用docker
容器初始化一个实例。为了运行集成测试,我们RabbitMQ
在测试用例开始时启动一个实例并在清理期间关闭它。
We're using TestContainers to do just that. Please see https://www.testcontainers.org/usage/dockerfile.htmland/or https://www.testcontainers.org/usage/docker_compose.html.
我们正在使用 TestContainers 来做到这一点。请参阅https://www.testcontainers.org/usage/dockerfile.html和/或https://www.testcontainers.org/usage/docker_compose.html。
回答by Michail Michailidis
Somewhat similar to Rajkishan's answerthat didn't work for me:
有点类似于Rajkishan 的答案,但对我不起作用:
This is what worked for me instead:
这对我有用:
@SpringBootApplication
public class MyTestsApp {
@Bean
@Primary
public CachingConnectionFactory rabbitAdmin() {
return Mockito.mock(CachingConnectionFactory.class);
}
}
@RunWith(SpringRunner.class)
@SpringBootTest(classes = {MyTestsApp.class})
@ActiveProfiles(profiles = "test")
public class MyTests {
}
回答by Vincent F
I was having a similar requirement at some point, and looked into QPid, which provides an in memory AMQP broker. It's forcing you to stay at the AMQP level, and use as little rabbitMq specific code as possible.
我在某个时候有类似的需求,并查看了 QPid,它提供了一个内存中的 AMQP 代理。它迫使您停留在 AMQP 级别,并尽可能少地使用 rabbitMq 特定代码。
But I actually found another way : by tweaking the names of queues and exchanges when running tests + the auto-delete value, we're not having the issue anymore. All queue/exchange names in tests are suffixed with the user name (of the account running the tests), meaning that everyone can run the tests on their machine without impacting others.
但我实际上找到了另一种方法:通过在运行测试时调整队列和交换的名称+自动删除值,我们不再有问题了。测试中的所有队列/交换名称都以用户名(运行测试的帐户的)为后缀,这意味着每个人都可以在自己的机器上运行测试而不会影响其他人。
Even in our CI pipeline, several projects may use the same exchanges/queues : we configure the values in tests to be project specific, so that even if 2 projects run their tests at the same time on the same machine with the same user, messages will not "leak" outside of current test.
即使在我们的 CI 管道中,几个项目也可能使用相同的交换/队列:我们将测试中的值配置为特定于项目,这样即使两个项目在同一台机器上以相同的用户同时运行它们的测试,消息不会在当前测试之外“泄漏”。
This ends up being much simpler to manage than mocking or spawning an in memory broker.
这最终比模拟或生成内存代理更容易管理。
回答by Dherik
First, create a @Configuration
with ConnectionFactory
in your test package:
首先,在您的测试包中创建一个@Configuration
with ConnectionFactory
:
@Configuration
public class RabbitMqConfiguration {
@Bean
ConnectionFactory connectionFactory() {
return new CachingConnectionFactory();
}
@Bean
public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory) {
return new RabbitTemplate(connectionFactory);
}
}
After that, set this property in your application.yml from test package:
之后,在测试包中的 application.yml 中设置此属性:
spring:
autoconfigure:
exclude: org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration
This should work for Spring Boot 2.2.x.
这应该适用于 Spring Boot 2.2.x。
For Spring Boot 1.5.x I also needed to add one more dependency:
对于 Spring Boot 1.5.x,我还需要再添加一个依赖项:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-stream-test-support</artifactId>
<scope>test</scope>
</dependency>
I don't know why, but without the spring-cloud-stream-test-support
dependency my Integration Test try to connect on RabbitMQ broker. Even not affecting the result of the test itself, this stole a lot of seconds in each test. I already see this strange behavior in another post.
我不知道为什么,但没有spring-cloud-stream-test-support
依赖我的集成测试尝试连接到 RabbitMQ 代理。即使不影响测试本身的结果,这在每次测试中也偷走了很多秒。我已经在另一篇文章中看到了这种奇怪的行为。