Testing Support

Spring AMQP 框架提供了单元和集成测试,可以使用模拟或实时 RabbitMQ 代理。可以查看这些测试用例以收集一些测试方案的思路。

编写异步应用程序的集成必须比测试更简单的应用程序复杂。当诸如 @RabbitListener 注解之类的抽象概念进入图片时,这变得更加复杂。问题是如何验证在发送消息后,侦听器是否按预期收到了消息。

Writing integration for asynchronous applications is necessarily more complex than testing simpler applications. This is made more complex when abstractions such as the @RabbitListener annotations come into the picture. The question is how to verify that, after sending a message, the listener received the message as expected.

框架本身有许多单元和集成测试。有些使用模拟,而另一些使用具有实时 RabbitMQ 代理的集成测试。你可以查看这些测试,了解一些测试方案的想法。

The framework itself has many unit and integration tests. Some using mocks while, others use integration testing with a live RabbitMQ broker. You can consult those tests for some ideas for testing scenarios.

Spring AMQP 版本 1.6 引入了 `spring-rabbit-test`jar,它提供了对其中某些更复杂场景的测试支持。预计此项目会随着时间的推移而扩展,但我们需要社区反馈以提出有关有助于测试所需功能的建议。请使用 JIRAGitHub Issues提供此类反馈。

Spring AMQP version 1.6 introduced the spring-rabbit-test jar, which provides support for testing some of these more complex scenarios. It is anticipated that this project will expand over time, but we need community feedback to make suggestions for the features needed to help with testing. Please use JIRA or GitHub Issues to provide such feedback.

@SpringRabbitTest

使用此注释向 Spring 测试 ApplicationContext 添加基础架构 Bean。使用例如 @SpringBootTest 时不需要这个,因为 Spring Boot 的自动配置将会添加这些 Bean。

Use this annotation to add infrastructure beans to the Spring test ApplicationContext. This is not necessary when using, for example @SpringBootTest since Spring Boot’s auto configuration will add the beans.

注册的 Bean 如下:

Beans that are registered are:

  • CachingConnectionFactory (autoConnectionFactory). If @RabbitEnabled is present, its connection factory is used.

  • RabbitTemplate (autoRabbitTemplate)

  • RabbitAdmin (autoRabbitAdmin)

  • RabbitListenerContainerFactory (autoContainerFactory)

此外,还添加了与 @EnableRabbit 关联的 Bean(以支持 @RabbitListener)。

In addition, the beans associated with @EnableRabbit (to support @RabbitListener) are added.

Junit5 example
@SpringJUnitConfig
@SpringRabbitTest
public class MyRabbitTests {

	@Autowired
	private RabbitTemplate template;

	@Autowired
	private RabbitAdmin admin;

	@Autowired
	private RabbitListenerEndpointRegistry registry;

	@Test
	void test() {
        ...
	}

	@Configuration
	public static class Config {

        ...

	}

}

对于 JUnit4,用 @RunWith(SpringRunnner.class) 替换 @SpringJUnitConfig

With JUnit4, replace @SpringJUnitConfig with @RunWith(SpringRunnner.class).

Mockito Answer<?> Implementations

目前有两种 Answer<?> 实现可帮助进行测试。

There are currently two Answer<?> implementations to help with testing.

第一个,LatchCountDownAndCallRealMethodAnswer,提供了一个 Answer<Void>,返回 null 并倒计数。以下示例显示了如何使用 LatchCountDownAndCallRealMethodAnswer

The first, LatchCountDownAndCallRealMethodAnswer, provides an Answer<Void> that returns null and counts down a latch. The following example shows how to use LatchCountDownAndCallRealMethodAnswer:

LatchCountDownAndCallRealMethodAnswer answer = this.harness.getLatchAnswerFor("myListener", 2);
doAnswer(answer)
    .when(listener).foo(anyString(), anyString());

...

assertThat(answer.await(10)).isTrue();

第二个,LambdaAnswer<T> 提供了一种机制,用于选择性地调用实际的方法,并提供了基于 InvocationOnMock 和结果(如果有)返回自定义结果的机会。

The second, LambdaAnswer<T> provides a mechanism to optionally call the real method and provides an opportunity to return a custom result, based on the InvocationOnMock and the result (if any).

考虑以下 POJO:

Consider the following POJO:

public class Thing {

    public String thing(String thing) {
        return thing.toUpperCase();
    }

}

以下类测试 Thing POJO:

The following class tests the Thing POJO:

Thing thing = spy(new Thing());

doAnswer(new LambdaAnswer<String>(true, (i, r) -> r + r))
    .when(thing).thing(anyString());
assertEquals("THINGTHING", thing.thing("thing"));

doAnswer(new LambdaAnswer<String>(true, (i, r) -> r + i.getArguments()[0]))
    .when(thing).thing(anyString());
assertEquals("THINGthing", thing.thing("thing"));

doAnswer(new LambdaAnswer<String>(false, (i, r) ->
    "" + i.getArguments()[0] + i.getArguments()[0])).when(thing).thing(anyString());
assertEquals("thingthing", thing.thing("thing"));

从 2.2.3 版开始,答案会捕获正在测试的方法抛出的任何异常。使用 answer.getExceptions() 获取它们的引用。

Starting with version 2.2.3, the answers capture any exceptions thrown by the method under test. Use answer.getExceptions() to get a reference to them.

当与 xref:testing.adoc#test-harness[@RabbitListenerTestRabbitListenerTestHarness 结合使用时,请使用 harness.getLambdaAnswerFor("listenerId", true, …​) 为侦听器获取正确构建的答案。

When used in conjunction with the @RabbitListenerTest and RabbitListenerTestHarness use harness.getLambdaAnswerFor("listenerId", true, …​) to get a properly constructed answer for the listener.

@RabbitListenerTest and RabbitListenerTestHarness

使用 @RabbitListenerTest 注释某个 @Configuration 类,会导致框架用一个称为 RabbitListenerTestHarness 的子类来替换标准的 RabbitListenerAnnotationBeanPostProcessor(它还通过 @EnableRabbit 启用 @RabbitListener 检测)。

Annotating one of your @Configuration classes with @RabbitListenerTest causes the framework to replace the standard RabbitListenerAnnotationBeanPostProcessor with a subclass called RabbitListenerTestHarness (it also enables @RabbitListener detection through @EnableRabbit).

RabbitListenerTestHarness 以两种方式增强侦听器。首先,它将侦听器包装在 Mockito Spy 中,启用正常的 Mockito 存根和验证操作。它还可以在侦听器中添加 Advice,来启用对参数、结果和任何抛出的异常的访问。您可以通过 @RabbitListenerTest 上的属性来控制启用哪些(或两者同时启用)。后者用于访问关于调用的较低级别的数据。它还支持阻塞测试线程,直至调用异步侦听器。

The RabbitListenerTestHarness enhances the listener in two ways. First, it wraps the listener in a Mockito Spy, enabling normal Mockito stubbing and verification operations. It can also add an Advice to the listener, enabling access to the arguments, result, and any exceptions that are thrown. You can control which (or both) of these are enabled with attributes on the @RabbitListenerTest. The latter is provided for access to lower-level data about the invocation. It also supports blocking the test thread until the async listener is called.

和方法无法被监视或建议。此外,只有带属性的侦听器可以被监视或建议。

final @RabbitListener methods cannot be spied or advised. Also, only listeners with an id attribute can be spied or advised.

考虑一些示例。

Consider some examples.

以下示例使用间谍:

The following example uses spy:

@Configuration
@RabbitListenerTest
public class Config {

    @Bean
    public Listener listener() {
        return new Listener();
    }

    ...

}

public class Listener {

    @RabbitListener(id="foo", queues="#{queue1.name}")
    public String foo(String foo) {
        return foo.toUpperCase();
    }

    @RabbitListener(id="bar", queues="#{queue2.name}")
    public void foo(@Payload String foo, @Header("amqp_receivedRoutingKey") String rk) {
        ...
    }

}

public class MyTests {

    @Autowired
    private RabbitListenerTestHarness harness; 1

    @Test
    public void testTwoWay() throws Exception {
        assertEquals("FOO", this.rabbitTemplate.convertSendAndReceive(this.queue1.getName(), "foo"));

        Listener listener = this.harness.getSpy("foo"); 2
        assertNotNull(listener);
        verify(listener).foo("foo");
    }

    @Test
    public void testOneWay() throws Exception {
        Listener listener = this.harness.getSpy("bar");
        assertNotNull(listener);

        LatchCountDownAndCallRealMethodAnswer answer = this.harness.getLatchAnswerFor("bar", 2); 3
        doAnswer(answer).when(listener).foo(anyString(), anyString()); 4

        this.rabbitTemplate.convertAndSend(this.queue2.getName(), "bar");
        this.rabbitTemplate.convertAndSend(this.queue2.getName(), "baz");

        assertTrue(answer.await(10));
        verify(listener).foo("bar", this.queue2.getName());
        verify(listener).foo("baz", this.queue2.getName());
    }

}
1 Inject the harness into the test case so we can get access to the spy.
2 Get a reference to the spy so we can verify it was invoked as expected. Since this is a send and receive operation, there is no need to suspend the test thread because it was already suspended in the RabbitTemplate waiting for the reply.
3 In this case, we’re only using a send operation so we need a latch to wait for the asynchronous call to the listener on the container thread. We use one of the Answer<?> implementations to help with that. IMPORTANT: Due to the way the listener is spied, it is important to use harness.getLatchAnswerFor() to get a properly configured answer for the spy.
4 Configure the spy to invoke the Answer.

以下示例使用捕获建议:

The following example uses the capture advice:

@Configuration
@ComponentScan
@RabbitListenerTest(spy = false, capture = true)
public class Config {

}

@Service
public class Listener {

    private boolean failed;

    @RabbitListener(id="foo", queues="#{queue1.name}")
    public String foo(String foo) {
        return foo.toUpperCase();
    }

    @RabbitListener(id="bar", queues="#{queue2.name}")
    public void foo(@Payload String foo, @Header("amqp_receivedRoutingKey") String rk) {
        if (!failed && foo.equals("ex")) {
            failed = true;
            throw new RuntimeException(foo);
        }
        failed = false;
    }

}

public class MyTests {

    @Autowired
    private RabbitListenerTestHarness harness; 1

    @Test
    public void testTwoWay() throws Exception {
        assertEquals("FOO", this.rabbitTemplate.convertSendAndReceive(this.queue1.getName(), "foo"));

        InvocationData invocationData =
            this.harness.getNextInvocationDataFor("foo", 0, TimeUnit.SECONDS); 2
        assertThat(invocationData.getArguments()[0], equalTo("foo"));     3
        assertThat((String) invocationData.getResult(), equalTo("FOO"));
    }

    @Test
    public void testOneWay() throws Exception {
        this.rabbitTemplate.convertAndSend(this.queue2.getName(), "bar");
        this.rabbitTemplate.convertAndSend(this.queue2.getName(), "baz");
        this.rabbitTemplate.convertAndSend(this.queue2.getName(), "ex");

        InvocationData invocationData =
            this.harness.getNextInvocationDataFor("bar", 10, TimeUnit.SECONDS); 4
        Object[] args = invocationData.getArguments();
        assertThat((String) args[0], equalTo("bar"));
        assertThat((String) args[1], equalTo(queue2.getName()));

        invocationData = this.harness.getNextInvocationDataFor("bar", 10, TimeUnit.SECONDS);
        args = invocationData.getArguments();
        assertThat((String) args[0], equalTo("baz"));

        invocationData = this.harness.getNextInvocationDataFor("bar", 10, TimeUnit.SECONDS);
        args = invocationData.getArguments();
        assertThat((String) args[0], equalTo("ex"));
        assertEquals("ex", invocationData.getThrowable().getMessage()); 5
    }

}
1 Inject the harness into the test case so we can get access to the spy.
2 Use harness.getNextInvocationDataFor() to retrieve the invocation data - in this case since it was a request/reply scenario there is no need to wait for any time because the test thread was suspended in the RabbitTemplate waiting for the result.
3 We can then verify that the argument and result was as expected.
4 This time we need some time to wait for the data, since it’s an async operation on the container thread and we need to suspend the test thread.
5 When the listener throws an exception, it is available in the throwable property of the invocation data.

在将自定义与测试工具配合使用时,为了正常运行,此类解答应当对进行子类化,并从测试工具获取实际侦听器(不是侦听器间谍),然后调用。有关示例,请参阅提供的实现源代码。

When using custom Answer<?> s with the harness, in order to operate properly, such answers should subclass ForwardsInvocation and get the actual listener (not the spy) from the harness (getDelegate("myListener")) and call super.answer(invocation). See the provided Mockito Answer<?> Implementations source code for examples.

Using TestRabbitTemplate

TestRabbitTemplate 用于执行一些基本集成测试,而无需代理。当您在测试用例中将其添加为 @Bean 时,它会发现上下文中所有侦听器容器,无论声明为 @Bean<bean/> 还是使用 @RabbitListener 注释。它目前仅支持按队列名称进行路由。该模板从容器中提取消息侦听器,并直接在测试线程上调用它。对于返回响应的侦听器,支持请求-响应消息传递(sendAndReceive 方法)。

The TestRabbitTemplate is provided to perform some basic integration testing without the need for a broker. When you add it as a @Bean in your test case, it discovers all the listener containers in the context, whether declared as @Bean or <bean/> or using the @RabbitListener annotation. It currently only supports routing by queue name. The template extracts the message listener from the container and invokes it directly on the test thread. Request-reply messaging (sendAndReceive methods) is supported for listeners that return replies.

以下测试用例使用该模板:

The following test case uses the template:

@RunWith(SpringRunner.class)
public class TestRabbitTemplateTests {

    @Autowired
    private TestRabbitTemplate template;

    @Autowired
    private Config config;

    @Test
    public void testSimpleSends() {
        this.template.convertAndSend("foo", "hello1");
        assertThat(this.config.fooIn, equalTo("foo:hello1"));
        this.template.convertAndSend("bar", "hello2");
        assertThat(this.config.barIn, equalTo("bar:hello2"));
        assertThat(this.config.smlc1In, equalTo("smlc1:"));
        this.template.convertAndSend("foo", "hello3");
        assertThat(this.config.fooIn, equalTo("foo:hello1"));
        this.template.convertAndSend("bar", "hello4");
        assertThat(this.config.barIn, equalTo("bar:hello2"));
        assertThat(this.config.smlc1In, equalTo("smlc1:hello3hello4"));

        this.template.setBroadcast(true);
        this.template.convertAndSend("foo", "hello5");
        assertThat(this.config.fooIn, equalTo("foo:hello1foo:hello5"));
        this.template.convertAndSend("bar", "hello6");
        assertThat(this.config.barIn, equalTo("bar:hello2bar:hello6"));
        assertThat(this.config.smlc1In, equalTo("smlc1:hello3hello4hello5hello6"));
    }

    @Test
    public void testSendAndReceive() {
        assertThat(this.template.convertSendAndReceive("baz", "hello"), equalTo("baz:hello"));
    }
    @Configuration
    @EnableRabbit
    public static class Config {

        public String fooIn = "";

        public String barIn = "";

        public String smlc1In = "smlc1:";

        @Bean
        public TestRabbitTemplate template() throws IOException {
            return new TestRabbitTemplate(connectionFactory());
        }

        @Bean
        public ConnectionFactory connectionFactory() throws IOException {
            ConnectionFactory factory = mock(ConnectionFactory.class);
            Connection connection = mock(Connection.class);
            Channel channel = mock(Channel.class);
            willReturn(connection).given(factory).createConnection();
            willReturn(channel).given(connection).createChannel(anyBoolean());
            given(channel.isOpen()).willReturn(true);
            return factory;
        }

        @Bean
        public SimpleRabbitListenerContainerFactory rabbitListenerContainerFactory() throws IOException {
            SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
            factory.setConnectionFactory(connectionFactory());
            return factory;
        }

        @RabbitListener(queues = "foo")
        public void foo(String in) {
            this.fooIn += "foo:" + in;
        }

        @RabbitListener(queues = "bar")
        public void bar(String in) {
            this.barIn += "bar:" + in;
        }

        @RabbitListener(queues = "baz")
        public String baz(String in) {
            return "baz:" + in;
        }

        @Bean
        public SimpleMessageListenerContainer smlc1() throws IOException {
            SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(connectionFactory());
            container.setQueueNames("foo", "bar");
            container.setMessageListener(new MessageListenerAdapter(new Object() {

                public void handleMessage(String in) {
                    smlc1In += in;
                }

            }));
            return container;
        }

    }

}

JUnit4 @Rules

Spring AMQP 1.7 及更高版本提供了一个名为 `spring-rabbit-junit`的额外 jar。此 jar 包含几个用于在运行 JUnit4 测试时使用的实用程序 `@Rule`实例。有关 JUnit5 测试,请参见 JUnit5 Conditions

Spring AMQP version 1.7 and later provide an additional jar called spring-rabbit-junit. This jar contains a couple of utility @Rule instances for use when running JUnit4 tests. See JUnit5 Conditions for JUnit5 testing.

Using BrokerRunning

BrokerRunning 提供了在代理(默认情况下为 localhost)未运行时,让测试成功执行的机制。

BrokerRunning provides a mechanism to let tests succeed when a broker is not running (on localhost, by default).

它还有用于初始化和清空队列及删除队列和交换的实用程序方法。

It also has utility methods to initialize and empty queues and delete queues and exchanges.

以下示例显示其用法:

The following example shows its usage:

@ClassRule
public static BrokerRunning brokerRunning = BrokerRunning.isRunningWithEmptyQueues("foo", "bar");

@AfterClass
public static void tearDown() {
    brokerRunning.removeTestQueues("some.other.queue.too") // removes foo, bar as well
}

有一些 isRunning…​ 静态方法,例如 isBrokerAndManagementRunning(),用于验证代理是否启用了管理插件。

There are several isRunning…​ static methods, such as isBrokerAndManagementRunning(), which verifies the broker has the management plugin enabled.

Configuring the Rule

有时,当没有代理(例如每晚的 CI 构建)时,您希望测试失败。要在运行时禁用规则,请将名为 RABBITMQ_SERVER_REQUIRED 的环境变量设置为 true

There are times when you want tests to fail if there is no broker, such as a nightly CI build. To disable the rule at runtime, set an environment variable called RABBITMQ_SERVER_REQUIRED to true.

您可以用 setter 或环境变量来覆盖代理属性,如主机名:

You can override the broker properties, such as hostname with either setters or environment variables:

以下示例显示如何用 setter 覆盖属性:

The following example shows how to override properties with setters:

@ClassRule
public static BrokerRunning brokerRunning = BrokerRunning.isRunningWithEmptyQueues("foo", "bar");

static {
    brokerRunning.setHostName("10.0.0.1")
}

@AfterClass
public static void tearDown() {
    brokerRunning.removeTestQueues("some.other.queue.too") // removes foo, bar as well
}

还可以通过设置以下环境变量来覆盖属性:

You can also override properties by setting the following environment variables:

public static final String BROKER_ADMIN_URI = "RABBITMQ_TEST_ADMIN_URI";
public static final String BROKER_HOSTNAME = "RABBITMQ_TEST_HOSTNAME";
public static final String BROKER_PORT = "RABBITMQ_TEST_PORT";
public static final String BROKER_USER = "RABBITMQ_TEST_USER";
public static final String BROKER_PW = "RABBITMQ_TEST_PASSWORD";
public static final String BROKER_ADMIN_USER = "RABBITMQ_TEST_ADMIN_USER";
public static final String BROKER_ADMIN_PW = "RABBITMQ_TEST_ADMIN_PASSWORD";

这些环境变量覆盖默认设置(amqp 为 localhost:5672,管理 REST API 为 http://localhost:15672/api/)。

These environment variables override the default settings (localhost:5672 for amqp and http://localhost:15672/api/ for the management REST API).

更改主机名会影响 amqpmanagement REST API 连接(除非明确设置管理 uri)。

Changing the host name affects both the amqp and management REST API connection (unless the admin uri is explicitly set).

BrokerRunning 还提供了一个名为 setEnvironmentVariableOverridesstatic 方法,该方法允许您传入包含这些变量的映射。它们会覆盖系统环境变量。如果您希望在多个测试套件中对测试使用不同的配置,这可能很有用。重要信息:该方法必须在调用创建规则实例的任何 isRunning() 静态方法之前被调用。变量值应用于此调用之后创建的所有实例。调用 clearEnvironmentVariableOverrides() 可将规则重置为使用默认值(包括任何实际环境变量)。

BrokerRunning also provides a static method called setEnvironmentVariableOverrides that lets you can pass in a map containing these variables. They override system environment variables. This might be useful if you wish to use different configuration for tests in multiple test suites. IMPORTANT: The method must be called before invoking any of the isRunning() static methods that create the rule instance. Variable values are applied to all instances created after this invocation. Invoke clearEnvironmentVariableOverrides() to reset the rule to use defaults (including any actual environment variables).

在你的测试用例中,你可以在创建连接工厂时使用 brokerRunninggetConnectionFactory() 返回规则的 RabbitMQ ConnectionFactory. 以下示例展示了如何操作:

In your test cases, you can use the brokerRunning when creating the connection factory; getConnectionFactory() returns the rule’s RabbitMQ ConnectionFactory. The following example shows how to do so:

@Bean
public CachingConnectionFactory rabbitConnectionFactory() {
    return new CachingConnectionFactory(brokerRunning.getConnectionFactory());
}

Using LongRunningIntegrationTest

LongRunningIntegrationTest 是用于禁用长时间运行测试的规则。你可能希望在开发者系统上使用这个规则,但是确保在比如每晚 CI 构建中禁用这个规则。

LongRunningIntegrationTest is a rule that disables long running tests. You might want to use this on a developer system but ensure that the rule is disabled on, for example, nightly CI builds.

以下示例显示其用法:

The following example shows its usage:

@Rule
public LongRunningIntegrationTest longTests = new LongRunningIntegrationTest();

要禁用运行时的规则,请将名为 RUN_LONG_INTEGRATION_TESTS 的环境变量设置为 true

To disable the rule at runtime, set an environment variable called RUN_LONG_INTEGRATION_TESTS to true.

JUnit5 Conditions

版本 2.0.2 引入了对 JUnit5 的支持。

Version 2.0.2 introduced support for JUnit5.

Using the @RabbitAvailable Annotation

这个类级别的注释与 junit-rules 中讨论的 BrokerRunning @Rule 类似。它由 RabbitAvailableCondition 处理。

This class-level annotation is similar to the BrokerRunning @Rule discussed in JUnit4 @Rules. It is processed by the RabbitAvailableCondition.

该注释有三个属性:

The annotation has three properties:

  • queues: An array of queues that are declared (and purged) before each test and deleted when all tests are complete.

  • management: Set this to true if your tests also require the management plugin installed on the broker.

  • purgeAfterEach: (Since version 2.2) when true (default), the queues will be purged between tests.

它用于检查代理是否可用,如果没有,则跳过测试。正如 Configuring the Rule 中所述,如果为 true ,名为 RABBITMQ_SERVER_REQUIRED 的环境变量会导致在没有代理时快速使测试失败。如 Configuring the Rule 中所述,可以使用环境变量配置条件。

It is used to check whether the broker is available and skip the tests if not. As discussed in Configuring the Rule, the environment variable called RABBITMQ_SERVER_REQUIRED, if true, causes the tests to fail fast if there is no broker. You can configure the condition by using environment variables as discussed in Configuring the Rule.

此外,RabbitAvailableCondition 支持对参数化测试构造函数和方法的参数解析。支持两种参数类型:

In addition, the RabbitAvailableCondition supports argument resolution for parameterized test constructors and methods. Two argument types are supported:

  • BrokerRunningSupport: The instance (before 2.2, this was a JUnit 4 BrokerRunning instance)

  • ConnectionFactory: The BrokerRunningSupport instance’s RabbitMQ connection factory

以下示例展示了这两种情况:

The following example shows both:

@RabbitAvailable(queues = "rabbitAvailableTests.queue")
public class RabbitAvailableCTORInjectionTests {

    private final ConnectionFactory connectionFactory;

    public RabbitAvailableCTORInjectionTests(BrokerRunningSupport brokerRunning) {
        this.connectionFactory = brokerRunning.getConnectionFactory();
    }

    @Test
    public void test(ConnectionFactory cf) throws Exception {
        assertSame(cf, this.connectionFactory);
        Connection conn = this.connectionFactory.newConnection();
        Channel channel = conn.createChannel();
        DeclareOk declareOk = channel.queueDeclarePassive("rabbitAvailableTests.queue");
        assertEquals(0, declareOk.getConsumerCount());
        channel.close();
        conn.close();
    }

}

前面的测试在框架本身中,它验证参数注入,并且该条件正确创建了队列。

The preceding test is in the framework itself and verifies the argument injection and that the condition created the queue properly.

一个实际的用户测试可能是这样:

A practical user test might be as follows:

@RabbitAvailable(queues = "rabbitAvailableTests.queue")
public class RabbitAvailableCTORInjectionTests {

    private final CachingConnectionFactory connectionFactory;

    public RabbitAvailableCTORInjectionTests(BrokerRunningSupport brokerRunning) {
        this.connectionFactory =
            new CachingConnectionFactory(brokerRunning.getConnectionFactory());
    }

    @Test
    public void test() throws Exception {
        RabbitTemplate template = new RabbitTemplate(this.connectionFactory);
        ...
    }
}

当你在一个测试类中使用 Spring 注释应用程序上下文时,你可以通过一个名为 RabbitAvailableCondition.getBrokerRunning() 的静态方法获取对条件的连接工厂的引用。

When you use a Spring annotation application context within a test class, you can get a reference to the condition’s connection factory through a static method called RabbitAvailableCondition.getBrokerRunning().

从 2.2 版本开始,getBrokerRunning() 返回 BrokerRunningSupport 对象;以前,返回 JUnit 4 BrokerRunnning 实例。新类具有与 BrokerRunning 相同的 API。

Starting with version 2.2, getBrokerRunning() returns a BrokerRunningSupport object; previously, the JUnit 4 BrokerRunnning instance was returned. The new class has the same API as BrokerRunning.

以下测试来自该框架并展示了用法:

The following test comes from the framework and demonstrates the usage:

@RabbitAvailable(queues = {
        RabbitTemplateMPPIntegrationTests.QUEUE,
        RabbitTemplateMPPIntegrationTests.REPLIES })
@SpringJUnitConfig
@DirtiesContext(classMode = ClassMode.AFTER_EACH_TEST_METHOD)
public class RabbitTemplateMPPIntegrationTests {

    public static final String QUEUE = "mpp.tests";

    public static final String REPLIES = "mpp.tests.replies";

    @Autowired
    private RabbitTemplate template;

    @Autowired
    private Config config;

    @Test
    public void test() {

        ...

    }

    @Configuration
    @EnableRabbit
    public static class Config {

        @Bean
        public CachingConnectionFactory cf() {
            return new CachingConnectionFactory(RabbitAvailableCondition
                    .getBrokerRunning()
                    .getConnectionFactory());
        }

        @Bean
        public RabbitTemplate template() {

            ...

        }

        @Bean
        public SimpleRabbitListenerContainerFactory
                            rabbitListenerContainerFactory() {

            ...

        }

        @RabbitListener(queues = QUEUE)
        public byte[] foo(byte[] in) {
            return in;
        }

    }

}

Using the @LongRunning Annotation

LongRunningIntegrationTest JUnit4 @Rule 类似,此注释会导致测试被跳过(直到环境变量(或系统属性)被设置为 true)。以下示例展示了如何使用它:

Similar to the LongRunningIntegrationTest JUnit4 @Rule, this annotation causes tests to be skipped unless an environment variable (or system property) is set to true. The following example shows how to use it:

@RabbitAvailable(queues = SimpleMessageListenerContainerLongTests.QUEUE)
@LongRunning
public class SimpleMessageListenerContainerLongTests {

    public static final String QUEUE = "SimpleMessageListenerContainerLongTests.queue";

...

}

默认情况下,变量是 RUN_LONG_INTEGRATION_TESTS,但你可以在注释的 value 属性中指定变量名。

By default, the variable is RUN_LONG_INTEGRATION_TESTS, but you can specify the variable name in the annotation’s value attribute.