From d73f4ffc0a2d866ea4f0349a5f48d9763f433a5f Mon Sep 17 00:00:00 2001 From: Gary Russell Date: Wed, 20 Dec 2017 14:43:35 -0500 Subject: [PATCH] AMQP-791: Support JUnit5 JIRA: https://jira.spring.io/browse/AMQP-791 - refactor `BrokerRunning` JUnit4 `@Rule` so it can be invoked from an `ExecutionCondition`. - Add `@RabbitAvailable` annotation with queue list and auto-delete queues at the end of the class; purge them between tests. * Implement `ParameterResolver` to access the rule's connection factory. * Support CTOR Injection and `BrokerRunning` Injection - user might want to invoke methods such as `deleteQueues()`. * Patches omitted from previous commit * WIP - Spring * Polishing - PR Comments * Convert `RabbitTemplateMPPIntegrationTests` - JUnit5 * Remove bogus test * Docs + `@LongRunning` * Polishing - PR Comments --- build.gradle | 27 +- .../amqp/rabbit/junit/BrokerRunning.java | 96 ++++-- .../amqp/rabbit/junit/LongRunning.java | 49 +++ .../junit/LongRunningIntegrationTest.java | 23 +- .../LongRunningIntegrationTestCondition.java | 58 ++++ .../amqp/rabbit/junit/RabbitAvailable.java | 53 +++ .../junit/RabbitAvailableCondition.java | 130 +++++++ .../src/main/resources/log4j2-test.xml | 15 + ...RunningIntegrationTestsConditionTests.java | 33 ++ .../RabbitAvailableCTORInjectionTests.java | 54 +++ .../rabbit/junit/RabbitAvailableTests.java | 46 +++ .../RabbitTemplateMPPIntegrationTests.java | 30 +- ...mpleMessageListenerContainerLongTests.java | 42 +-- src/reference/asciidoc/amqp.adoc | 14 +- src/reference/asciidoc/testing.adoc | 320 +++++++++++++----- 15 files changed, 823 insertions(+), 167 deletions(-) create mode 100644 spring-rabbit-junit/src/main/java/org/springframework/amqp/rabbit/junit/LongRunning.java create mode 100644 spring-rabbit-junit/src/main/java/org/springframework/amqp/rabbit/junit/LongRunningIntegrationTestCondition.java create mode 100644 spring-rabbit-junit/src/main/java/org/springframework/amqp/rabbit/junit/RabbitAvailable.java create mode 100644 spring-rabbit-junit/src/main/java/org/springframework/amqp/rabbit/junit/RabbitAvailableCondition.java create mode 100644 spring-rabbit-junit/src/main/resources/log4j2-test.xml create mode 100644 spring-rabbit-junit/src/test/java/org/springframework/amqp/rabbit/junit/LongRunningIntegrationTestsConditionTests.java create mode 100644 spring-rabbit-junit/src/test/java/org/springframework/amqp/rabbit/junit/RabbitAvailableCTORInjectionTests.java create mode 100644 spring-rabbit-junit/src/test/java/org/springframework/amqp/rabbit/junit/RabbitAvailableTests.java diff --git a/build.gradle b/build.gradle index 5ff87e7f..976010d8 100644 --- a/build.gradle +++ b/build.gradle @@ -8,6 +8,7 @@ buildscript { classpath 'me.champeau.gradle:gradle-javadoc-hotfix-plugin:0.1' classpath 'io.spring.gradle:dependency-management-plugin:1.0.2.RELEASE' classpath 'io.spring.gradle:spring-io-plugin:0.0.8.RELEASE' + classpath 'org.junit.platform:junit-platform-gradle-plugin:1.0.2' } } @@ -87,7 +88,10 @@ subprojects { subproject -> ext { hamcrestVersion = '1.3' jackson2Version = '2.9.1' - junitVersion = '4.12' + junit4Version = '4.12' + junitJupiterVersion = '5.0.2' + junitPlatformVersion = '1.0.2' + junitVintageVersion = '4.12.2' log4jVersion = '2.8.2' logbackVersion = '1.2.3' mockitoVersion = '2.11.0' @@ -112,7 +116,7 @@ subprojects { subproject -> // dependencies that are common across all java projects dependencies { - testCompile ("junit:junit:$junitVersion") { + testCompile ("junit:junit:$junit4Version") { exclude group: 'org.hamcrest', module: 'hamcrest-core' } testCompile "org.apache.logging.log4j:log4j-core:$log4jVersion" @@ -123,6 +127,18 @@ subprojects { subproject -> testCompile "org.springframework:spring-test:$springVersion" testCompile "org.slf4j:slf4j-log4j12:$slf4jVersion" // amqp-client now uses SLF4J testRuntime "org.apache.logging.log4j:log4j-jcl:$log4jVersion" + + testCompile "org.junit.jupiter:junit-jupiter-api:${junitJupiterVersion}" + testRuntime "org.junit.jupiter:junit-jupiter-engine:${junitJupiterVersion}" + testRuntime "org.junit.platform:junit-platform-commons:${junitPlatformVersion}" + testRuntime "org.junit.platform:junit-platform-launcher:${junitPlatformVersion}" + + // To support JUnit 4 tests + testRuntime "org.junit.vintage:junit-vintage-engine:${junitVintageVersion}" + + // To avoid compiler warnings about @API annotations in JUnit code + testCompileOnly 'org.apiguardian:apiguardian-api:1.0.0' + } // enable all compiler warnings; individual projects may customize further @@ -265,12 +281,15 @@ project('spring-rabbit-junit') { dependencies { // no spring-amqp dependencies allowed compile "org.springframework:spring-core:$springVersion" - compile "junit:junit:$junitVersion" + compile "junit:junit:$junit4Version" compile "com.rabbitmq:amqp-client:$rabbitmqVersion" compile ("com.rabbitmq:http-client:$rabbitmqHttpClientVersion") { exclude group: 'org.springframework', module: 'spring-web' } compile "org.springframework:spring-web:$springVersion" + compile "org.junit.jupiter:junit-jupiter-api:${junitJupiterVersion}" + compile "org.junit.platform:junit-platform-commons:${junitPlatformVersion}" + compileOnly 'org.apiguardian:apiguardian-api:1.0.0' } @@ -282,7 +301,7 @@ project('spring-rabbit-test') { dependencies { compile project(":spring-rabbit") - compile ("junit:junit:$junitVersion") { + compile ("junit:junit:$junit4Version") { exclude group: 'org.hamcrest', module: 'hamcrest-core' } compile "org.hamcrest:hamcrest-all:$hamcrestVersion" diff --git a/spring-rabbit-junit/src/main/java/org/springframework/amqp/rabbit/junit/BrokerRunning.java b/spring-rabbit-junit/src/main/java/org/springframework/amqp/rabbit/junit/BrokerRunning.java index b49c44b4..d6562a15 100644 --- a/spring-rabbit-junit/src/main/java/org/springframework/amqp/rabbit/junit/BrokerRunning.java +++ b/spring-rabbit-junit/src/main/java/org/springframework/amqp/rabbit/junit/BrokerRunning.java @@ -19,6 +19,8 @@ package org.springframework.amqp.rabbit.junit; import static org.junit.Assert.fail; import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URISyntaxException; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Arrays; @@ -367,38 +369,8 @@ public final class BrokerRunning extends TestWatcher { Channel channel = null; try { - connection = connectionFactory.newConnection(); - connection.setId(generateId()); - channel = connection.createChannel(); - - for (String queueName : this.queues) { - - if (this.purge) { - logger.debug("Deleting queue: " + queueName); - // Delete completely - gets rid of consumers and bindings as well - channel.queueDelete(queueName); - } - - if (isDefaultQueue(queueName)) { - // Just for test probe. - channel.queueDelete(queueName); - } - else { - channel.queueDeclare(queueName, true, false, false, null); - } - } - brokerOffline.put(this.port, false); - if (!this.assumeOnline) { - Assume.assumeTrue(brokerOffline.get(this.port)); - } - - if (this.management) { - Client client = new Client(getAdminUri(), this.adminUser, this.adminPassword); - if (!client.alivenessTest("/")) { - throw new RuntimeException("Aliveness test failed for localhost:15672 guest/quest; " - + "management not available"); - } - } + connection = getConnection(connectionFactory); + channel = createQueues(connection); } catch (Exception e) { logger.warn("Not executing tests because basic connectivity test failed: " + e.getMessage()); @@ -419,7 +391,59 @@ public final class BrokerRunning extends TestWatcher { return super.apply(base, description); } - private boolean fatal() { + public void isUp() throws Exception { + Connection connection = getConnectionFactory().newConnection(); + Channel channel = null; + try { + channel = createQueues(connection); + } + finally { + closeResources(connection, channel); + } + } + + private Connection getConnection(ConnectionFactory connectionFactory) throws IOException, TimeoutException { + Connection connection = connectionFactory.newConnection(); + connection.setId(generateId()); + return connection; + } + + private Channel createQueues(Connection connection) throws IOException, MalformedURLException, URISyntaxException { + Channel channel; + channel = connection.createChannel(); + + for (String queueName : this.queues) { + + if (this.purge) { + logger.debug("Deleting queue: " + queueName); + // Delete completely - gets rid of consumers and bindings as well + channel.queueDelete(queueName); + } + + if (isDefaultQueue(queueName)) { + // Just for test probe. + channel.queueDelete(queueName); + } + else { + channel.queueDeclare(queueName, true, false, false, null); + } + } + brokerOffline.put(this.port, false); + if (!this.assumeOnline) { + Assume.assumeTrue(brokerOffline.get(this.port)); + } + + if (this.management) { + Client client = new Client(getAdminUri(), this.adminUser, this.adminPassword); + if (!client.alivenessTest("/")) { + throw new RuntimeException("Aliveness test failed for localhost:15672 guest/quest; " + + "management not available"); + } + } + return channel; + } + + public static boolean fatal() { String serversRequired = System.getenv(BROKER_REQUIRED); if (Boolean.parseBoolean(serversRequired)) { logger.error("RABBITMQ IS REQUIRED BUT NOT AVAILABLE"); @@ -465,7 +489,7 @@ public final class BrokerRunning extends TestWatcher { Channel channel = null; try { - connection = connectionFactory.newConnection(); + connection = getConnection(connectionFactory); connection.setId(generateId() + ".queueDelete"); channel = connection.createChannel(); @@ -491,7 +515,7 @@ public final class BrokerRunning extends TestWatcher { Channel channel = null; try { - connection = connectionFactory.newConnection(); + connection = getConnection(connectionFactory); connection.setId(generateId() + ".queueDelete"); channel = connection.createChannel(); @@ -517,7 +541,7 @@ public final class BrokerRunning extends TestWatcher { Channel channel = null; try { - connection = connectionFactory.newConnection(); + connection = getConnection(connectionFactory); connection.setId(generateId() + ".exchangeDelete"); channel = connection.createChannel(); diff --git a/spring-rabbit-junit/src/main/java/org/springframework/amqp/rabbit/junit/LongRunning.java b/spring-rabbit-junit/src/main/java/org/springframework/amqp/rabbit/junit/LongRunning.java new file mode 100644 index 00000000..c82791e3 --- /dev/null +++ b/spring-rabbit-junit/src/main/java/org/springframework/amqp/rabbit/junit/LongRunning.java @@ -0,0 +1,49 @@ +/* + * Copyright 2017 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.amqp.rabbit.junit; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.junit.jupiter.api.extension.ExtendWith; + +/** + * Test classes annotated with this will not run if an environment variable or system + * property (default {@code RUN_LONG_INTEGRATION_TESTS}) is not present or does not have + * the value that {@link Boolean#parseBoolean(String)} evaluates to {@code true}. + * + * @author Gary Russell + * @since 2.0.2 + * + */ +@ExtendWith(LongRunningIntegrationTestCondition.class) +@Target({ ElementType.TYPE }) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface LongRunning { + + /** + * The name of the variable/property used to determine whether long runnning tests + * should run. + * @return the name of the variable/property. + */ + String value() default LongRunningIntegrationTest.RUN_LONG_INTEGRATION_TESTS; + +} diff --git a/spring-rabbit-junit/src/main/java/org/springframework/amqp/rabbit/junit/LongRunningIntegrationTest.java b/spring-rabbit-junit/src/main/java/org/springframework/amqp/rabbit/junit/LongRunningIntegrationTest.java index 152f7d44..e665ed31 100644 --- a/spring-rabbit-junit/src/main/java/org/springframework/amqp/rabbit/junit/LongRunningIntegrationTest.java +++ b/spring-rabbit-junit/src/main/java/org/springframework/amqp/rabbit/junit/LongRunningIntegrationTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2016 the original author or authors. + * Copyright 2013-2017 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -36,12 +36,21 @@ public class LongRunningIntegrationTest extends TestWatcher { private final static Log logger = LogFactory.getLog(LongRunningIntegrationTest.class); - private static final String RUN_LONG_PROP = "RUN_LONG_INTEGRATION_TESTS"; + public static final String RUN_LONG_INTEGRATION_TESTS = "RUN_LONG_INTEGRATION_TESTS"; private boolean shouldRun = false; public LongRunningIntegrationTest() { - for (String value: new String[] { System.getenv(RUN_LONG_PROP), System.getProperty(RUN_LONG_PROP) }) { + this(RUN_LONG_INTEGRATION_TESTS); + } + + /** + * Check using a custom variable/property name. + * @param property the variable/property name. + * @since 2.0.2 + */ + public LongRunningIntegrationTest(String property) { + for (String value: new String[] { System.getenv(property), System.getProperty(property) }) { if (Boolean.parseBoolean(value)) { this.shouldRun = true; break; @@ -58,4 +67,12 @@ public class LongRunningIntegrationTest extends TestWatcher { return super.apply(base, description); } + /** + * Return true if the test should run. + * @return true to run. + */ + public boolean isShouldRun() { + return this.shouldRun; + } + } diff --git a/spring-rabbit-junit/src/main/java/org/springframework/amqp/rabbit/junit/LongRunningIntegrationTestCondition.java b/spring-rabbit-junit/src/main/java/org/springframework/amqp/rabbit/junit/LongRunningIntegrationTestCondition.java new file mode 100644 index 00000000..778a3cdb --- /dev/null +++ b/spring-rabbit-junit/src/main/java/org/springframework/amqp/rabbit/junit/LongRunningIntegrationTestCondition.java @@ -0,0 +1,58 @@ +/* + * Copyright 2017 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.amqp.rabbit.junit; + +import java.lang.reflect.AnnotatedElement; +import java.util.Optional; + +import org.junit.jupiter.api.extension.ConditionEvaluationResult; +import org.junit.jupiter.api.extension.ExecutionCondition; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.platform.commons.util.AnnotationUtils; + +import org.springframework.util.StringUtils; + +/** + * {@link ExecutionCondition} to skip long running tests unless an environment + * variable or property is set. + * + * @author Gary Russell + * @since 2.0.2 + * @see LongRunning + */ +public class LongRunningIntegrationTestCondition implements ExecutionCondition { + + private static final ConditionEvaluationResult ENABLED = ConditionEvaluationResult.enabled( + "@LongRunning is not present"); + + @Override + public ConditionEvaluationResult evaluateExecutionCondition(ExtensionContext context) { + Optional element = context.getElement(); + Optional longRunning = AnnotationUtils.findAnnotation(element, LongRunning.class); + if (longRunning.isPresent()) { + String property = longRunning.get().value(); + if (!StringUtils.hasText(property)) { + property = LongRunningIntegrationTest.RUN_LONG_INTEGRATION_TESTS; + } + LongRunningIntegrationTest lrit = new LongRunningIntegrationTest(property); + return lrit.isShouldRun() ? ConditionEvaluationResult.enabled("Long running tests must run") + : ConditionEvaluationResult.disabled("Long running tests are skipped"); + } + return ENABLED; + } + +} diff --git a/spring-rabbit-junit/src/main/java/org/springframework/amqp/rabbit/junit/RabbitAvailable.java b/spring-rabbit-junit/src/main/java/org/springframework/amqp/rabbit/junit/RabbitAvailable.java new file mode 100644 index 00000000..9bc20301 --- /dev/null +++ b/spring-rabbit-junit/src/main/java/org/springframework/amqp/rabbit/junit/RabbitAvailable.java @@ -0,0 +1,53 @@ +/* + * Copyright 2017 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.amqp.rabbit.junit; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.junit.jupiter.api.extension.ExtendWith; + +/** + * Test classes annotated with this will not run if there is no broker on localhost. + * + * @author Gary Russell + * @since 2.0.2 + * + */ +@ExtendWith(RabbitAvailableCondition.class) +@Target({ ElementType.TYPE }) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface RabbitAvailable { + + /** + * The queues to create and ensure empty; they will be deleted after the test class + * completes. + * @return the queues. + */ + String[] queues() default {}; + + /** + * Requires the management plugin to be available. + * @return true to require a management plugin. + */ + boolean management() default false; + +} diff --git a/spring-rabbit-junit/src/main/java/org/springframework/amqp/rabbit/junit/RabbitAvailableCondition.java b/spring-rabbit-junit/src/main/java/org/springframework/amqp/rabbit/junit/RabbitAvailableCondition.java new file mode 100644 index 00000000..bf8bf04e --- /dev/null +++ b/spring-rabbit-junit/src/main/java/org/springframework/amqp/rabbit/junit/RabbitAvailableCondition.java @@ -0,0 +1,130 @@ +/* + * Copyright 2017 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.amqp.rabbit.junit; + +import java.lang.reflect.AnnotatedElement; +import java.util.Optional; + +import org.junit.jupiter.api.extension.AfterAllCallback; +import org.junit.jupiter.api.extension.ConditionEvaluationResult; +import org.junit.jupiter.api.extension.ExecutionCondition; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.api.extension.ExtensionContext.Namespace; +import org.junit.jupiter.api.extension.ExtensionContext.Store; +import org.junit.jupiter.api.extension.ParameterContext; +import org.junit.jupiter.api.extension.ParameterResolutionException; +import org.junit.jupiter.api.extension.ParameterResolver; +import org.junit.platform.commons.util.AnnotationUtils; + +import org.springframework.util.Assert; + +import com.rabbitmq.client.ConnectionFactory; + +/** + * JUnit5 {@link ExecutionCondition}. + * Looks for {@code @RabbitAvailable} annotated classes and disables + * if found the broker is not available. + * + * @author Gary Russell + * @since 2.0.2 + * + */ +public class RabbitAvailableCondition implements ExecutionCondition, AfterAllCallback, ParameterResolver { + + private static final ConditionEvaluationResult ENABLED = ConditionEvaluationResult.enabled( + "@RabbitAvailable is not present"); + + private static final ThreadLocal brokerRunningHolder = new ThreadLocal<>(); + + @Override + public ConditionEvaluationResult evaluateExecutionCondition(ExtensionContext context) { + Optional element = context.getElement(); + Optional rabbit = AnnotationUtils.findAnnotation(element, RabbitAvailable.class); + if (rabbit.isPresent()) { + try { + String[] queues = rabbit.get().queues(); + BrokerRunning brokerRunning = getStore(context).get("brokerRunning", BrokerRunning.class); + if (brokerRunning == null) { + if (rabbit.get().management()) { + brokerRunning = BrokerRunning.isBrokerAndManagementRunningWithEmptyQueues(queues); + } + else { + brokerRunning = BrokerRunning.isRunningWithEmptyQueues(queues); + } + } + brokerRunning.isUp(); + brokerRunningHolder.set(brokerRunning); + Store store = getStore(context); + store.put("brokerRunning", brokerRunning); + store.put("queuesToDelete", queues); + return ConditionEvaluationResult.enabled("RabbitMQ is available"); + } + catch (Exception e) { + if (BrokerRunning.fatal()) { + throw new IllegalStateException("Required RabbitMQ is not available"); + } + return ConditionEvaluationResult.disabled("RabbitMQ is not available"); + } + } + return ENABLED; + } + + @Override + public void afterAll(ExtensionContext context) throws Exception { + brokerRunningHolder.remove(); + Store store = getStore(context); + BrokerRunning brokerRunning = store.remove("brokerRunning", BrokerRunning.class); + if (brokerRunning != null) { + brokerRunning.removeTestQueues(); + } + } + + @Override + public boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) + throws ParameterResolutionException { + Class type = parameterContext.getParameter().getType(); + return type.equals(ConnectionFactory.class) || type.equals(BrokerRunning.class); + } + + @Override + public Object resolveParameter(ParameterContext parameterContext, ExtensionContext context) + throws ParameterResolutionException { + // in parent for method injection, Composite key causes a store miss + BrokerRunning brokerRunning = + getParentStore(context).get("brokerRunning", BrokerRunning.class) == null + ? getStore(context).get("brokerRunning", BrokerRunning.class) + : getParentStore(context).get("brokerRunning", BrokerRunning.class); + Assert.state(brokerRunning != null, "Could not find brokerRunning instance"); + Class type = parameterContext.getParameter().getType(); + return type.equals(ConnectionFactory.class) ? brokerRunning.getConnectionFactory() + : brokerRunning; + } + + private Store getStore(ExtensionContext context) { + return context.getStore(Namespace.create(getClass(), context)); + } + + private Store getParentStore(ExtensionContext context) { + ExtensionContext parent = context.getParent().get(); + return parent.getStore(Namespace.create(getClass(), parent)); + } + + public static BrokerRunning getBrokerRunning() { + return brokerRunningHolder.get(); + } + +} diff --git a/spring-rabbit-junit/src/main/resources/log4j2-test.xml b/spring-rabbit-junit/src/main/resources/log4j2-test.xml new file mode 100644 index 00000000..86ded2e3 --- /dev/null +++ b/spring-rabbit-junit/src/main/resources/log4j2-test.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/spring-rabbit-junit/src/test/java/org/springframework/amqp/rabbit/junit/LongRunningIntegrationTestsConditionTests.java b/spring-rabbit-junit/src/test/java/org/springframework/amqp/rabbit/junit/LongRunningIntegrationTestsConditionTests.java new file mode 100644 index 00000000..4121e6d0 --- /dev/null +++ b/spring-rabbit-junit/src/test/java/org/springframework/amqp/rabbit/junit/LongRunningIntegrationTestsConditionTests.java @@ -0,0 +1,33 @@ +/* + * Copyright 2017 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.amqp.rabbit.junit; + +import org.junit.jupiter.api.Test; + +/** + * @author Gary Russell + * @since 2.0.2 + * + */ +@LongRunning("LongRunningIntegrationTestsConditionTests") +public class LongRunningIntegrationTestsConditionTests { + + @Test + public void test() { + } + +} diff --git a/spring-rabbit-junit/src/test/java/org/springframework/amqp/rabbit/junit/RabbitAvailableCTORInjectionTests.java b/spring-rabbit-junit/src/test/java/org/springframework/amqp/rabbit/junit/RabbitAvailableCTORInjectionTests.java new file mode 100644 index 00000000..0ca112be --- /dev/null +++ b/spring-rabbit-junit/src/test/java/org/springframework/amqp/rabbit/junit/RabbitAvailableCTORInjectionTests.java @@ -0,0 +1,54 @@ +/* + * Copyright 2017 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.amqp.rabbit.junit; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertSame; + +import org.junit.jupiter.api.Test; + +import com.rabbitmq.client.AMQP.Queue.DeclareOk; +import com.rabbitmq.client.Channel; +import com.rabbitmq.client.Connection; +import com.rabbitmq.client.ConnectionFactory; + +/** + * @author Gary Russell + * @since 2.0.2 + * + */ +@RabbitAvailable(queues = "rabbitAvailableTests.queue") +public class RabbitAvailableCTORInjectionTests { + + private final ConnectionFactory connectionFactory; + + public RabbitAvailableCTORInjectionTests(BrokerRunning 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(); + } + +} diff --git a/spring-rabbit-junit/src/test/java/org/springframework/amqp/rabbit/junit/RabbitAvailableTests.java b/spring-rabbit-junit/src/test/java/org/springframework/amqp/rabbit/junit/RabbitAvailableTests.java new file mode 100644 index 00000000..6a3e956b --- /dev/null +++ b/spring-rabbit-junit/src/test/java/org/springframework/amqp/rabbit/junit/RabbitAvailableTests.java @@ -0,0 +1,46 @@ +/* + * Copyright 2017 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.amqp.rabbit.junit; + +import static org.junit.Assert.assertEquals; + +import org.junit.jupiter.api.Test; + +import com.rabbitmq.client.AMQP.Queue.DeclareOk; +import com.rabbitmq.client.Channel; +import com.rabbitmq.client.Connection; +import com.rabbitmq.client.ConnectionFactory; + +/** + * @author Gary Russell + * @since 2.0.2 + * + */ +@RabbitAvailable(queues = "rabbitAvailableTests.queue") +public class RabbitAvailableTests { + + @Test + public void test(ConnectionFactory connectionFactory) throws Exception { + Connection conn = connectionFactory.newConnection(); + Channel channel = conn.createChannel(); + DeclareOk declareOk = channel.queueDeclarePassive("rabbitAvailableTests.queue"); + assertEquals(0, declareOk.getConsumerCount()); + channel.close(); + conn.close(); + } + +} diff --git a/spring-rabbit/src/test/java/org/springframework/amqp/rabbit/core/RabbitTemplateMPPIntegrationTests.java b/spring-rabbit/src/test/java/org/springframework/amqp/rabbit/core/RabbitTemplateMPPIntegrationTests.java index 8af4ae77..31164956 100644 --- a/spring-rabbit/src/test/java/org/springframework/amqp/rabbit/core/RabbitTemplateMPPIntegrationTests.java +++ b/spring-rabbit/src/test/java/org/springframework/amqp/rabbit/core/RabbitTemplateMPPIntegrationTests.java @@ -18,10 +18,7 @@ package org.springframework.amqp.rabbit.core; import static org.junit.Assert.assertTrue; -import org.junit.AfterClass; -import org.junit.ClassRule; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.Test; import org.springframework.amqp.core.Message; import org.springframework.amqp.core.MessagePostProcessor; @@ -30,30 +27,31 @@ import org.springframework.amqp.rabbit.annotation.EnableRabbit; import org.springframework.amqp.rabbit.annotation.RabbitListener; import org.springframework.amqp.rabbit.config.SimpleRabbitListenerContainerFactory; import org.springframework.amqp.rabbit.connection.CachingConnectionFactory; -import org.springframework.amqp.rabbit.junit.BrokerRunning; +import org.springframework.amqp.rabbit.junit.RabbitAvailable; +import org.springframework.amqp.rabbit.junit.RabbitAvailableCondition; import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.annotation.DirtiesContext.ClassMode; -import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.test.context.junit.jupiter.SpringJUnitConfig; /** * @author Gary Russell * @since 1.7.6 * */ -@RunWith(SpringRunner.class) +@RabbitAvailable(queues = { + RabbitTemplateMPPIntegrationTests.QUEUE, + RabbitTemplateMPPIntegrationTests.REPLIES }) +@SpringJUnitConfig @DirtiesContext(classMode = ClassMode.AFTER_EACH_TEST_METHOD) public class RabbitTemplateMPPIntegrationTests { - private static final String QUEUE = "mpp.tests"; + public static final String QUEUE = "mpp.tests"; - private static final String REPLIES = "mpp.tests.replies"; - - @ClassRule - public static BrokerRunning brokerIsRunning = BrokerRunning.isRunningWithEmptyQueues(QUEUE, REPLIES); + public static final String REPLIES = "mpp.tests.replies"; @Autowired private RabbitTemplate template; @@ -61,11 +59,6 @@ public class RabbitTemplateMPPIntegrationTests { @Autowired private Config config; - @AfterClass - public static void tearDown() { - brokerIsRunning.removeTestQueues(); - } - @Test // 2.0.x only public void testMPPsAppliedDirectReplyToContainerTests() { this.template.sendAndReceive(new Message("foo".getBytes(), new MessageProperties())); @@ -119,7 +112,8 @@ public class RabbitTemplateMPPIntegrationTests { @Bean public CachingConnectionFactory cf() { - return new CachingConnectionFactory(brokerIsRunning.getConnectionFactory()); + return new CachingConnectionFactory(RabbitAvailableCondition.getBrokerRunning() + .getConnectionFactory()); } @Bean diff --git a/spring-rabbit/src/test/java/org/springframework/amqp/rabbit/listener/SimpleMessageListenerContainerLongTests.java b/spring-rabbit/src/test/java/org/springframework/amqp/rabbit/listener/SimpleMessageListenerContainerLongTests.java index 2f46b06d..f47155fa 100644 --- a/spring-rabbit/src/test/java/org/springframework/amqp/rabbit/listener/SimpleMessageListenerContainerLongTests.java +++ b/spring-rabbit/src/test/java/org/springframework/amqp/rabbit/listener/SimpleMessageListenerContainerLongTests.java @@ -23,40 +23,40 @@ import java.util.Set; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.junit.After; -import org.junit.Rule; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.springframework.amqp.core.MessageListener; import org.springframework.amqp.core.Queue; import org.springframework.amqp.rabbit.connection.SingleConnectionFactory; import org.springframework.amqp.rabbit.core.RabbitAdmin; import org.springframework.amqp.rabbit.core.RabbitTemplate; -import org.springframework.amqp.rabbit.junit.BrokerRunning; -import org.springframework.amqp.rabbit.junit.LongRunningIntegrationTest; +import org.springframework.amqp.rabbit.junit.LongRunning; +import org.springframework.amqp.rabbit.junit.RabbitAvailable; import org.springframework.amqp.rabbit.listener.adapter.MessageListenerAdapter; import org.springframework.amqp.utils.test.TestUtils; import org.springframework.test.util.ReflectionTestUtils; +import com.rabbitmq.client.ConnectionFactory; + /** * @author Gary Russell * * @since 1.2.1 * */ +@RabbitAvailable(queues = SimpleMessageListenerContainerLongTests.QUEUE) +@LongRunning public class SimpleMessageListenerContainerLongTests { + public static final String QUEUE = "SimpleMessageListenerContainerLongTests.queue"; + private final Log logger = LogFactory.getLog(SimpleMessageListenerContainerLongTests.class); - @Rule - public LongRunningIntegrationTest longTest = new LongRunningIntegrationTest(); + private final SingleConnectionFactory connectionFactory; - @Rule - public BrokerRunning brokerRunning = BrokerRunning.isRunningWithEmptyQueues("foo"); - @After - public void tearDown() { - this.brokerRunning.removeTestQueues(); + public SimpleMessageListenerContainerLongTests(ConnectionFactory connectionFactory) { + this.connectionFactory = new SingleConnectionFactory(connectionFactory); } @Test @@ -70,11 +70,11 @@ public class SimpleMessageListenerContainerLongTests { } private void testChangeConsumerCountGuts(boolean transacted) throws Exception { - final SingleConnectionFactory singleConnectionFactory = new SingleConnectionFactory("localhost"); - SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(singleConnectionFactory); + SimpleMessageListenerContainer container = + new SimpleMessageListenerContainer(this.connectionFactory); try { container.setMessageListener(new MessageListenerAdapter(this)); - container.setQueueNames("foo"); + container.setQueueNames(QUEUE); container.setAutoStartup(false); container.setConcurrentConsumers(2); container.setChannelTransacted(transacted); @@ -85,9 +85,9 @@ public class SimpleMessageListenerContainerLongTests { container.setConcurrentConsumers(1); waitForNConsumers(container, 1); container.setMaxConcurrentConsumers(3); - RabbitTemplate template = new RabbitTemplate(singleConnectionFactory); + RabbitTemplate template = new RabbitTemplate(this.connectionFactory); for (int i = 0; i < 20; i++) { - template.convertAndSend("foo", "foo"); + template.convertAndSend(QUEUE, "foo"); } waitForNConsumers(container, 2); // increased consumers due to work waitForNConsumers(container, 1, 20000); // should stop the extra consumer after 10 seconds idle @@ -95,7 +95,7 @@ public class SimpleMessageListenerContainerLongTests { waitForNConsumers(container, 3); container.stop(); waitForNConsumers(container, 0); - singleConnectionFactory.destroy(); + this.connectionFactory.destroy(); } finally { container.stop(); @@ -104,13 +104,13 @@ public class SimpleMessageListenerContainerLongTests { @Test public void testAddQueuesAndStartInCycle() throws Exception { - final SingleConnectionFactory connectionFactory = new SingleConnectionFactory("localhost"); - final SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(connectionFactory); + final SimpleMessageListenerContainer container = new SimpleMessageListenerContainer( + this.connectionFactory); container.setMessageListener((MessageListener) message -> { }); container.setConcurrentConsumers(2); container.afterPropertiesSet(); - RabbitAdmin admin = new RabbitAdmin(connectionFactory); + RabbitAdmin admin = new RabbitAdmin(this.connectionFactory); for (int i = 0; i < 20; i++) { Queue queue = new Queue("testAddQueuesAndStartInCycle" + i); admin.declareQueue(queue); diff --git a/src/reference/asciidoc/amqp.adoc b/src/reference/asciidoc/amqp.adoc index e9c4eb81..a7c89192 100644 --- a/src/reference/asciidoc/amqp.adoc +++ b/src/reference/asciidoc/amqp.adoc @@ -4569,7 +4569,8 @@ The default retry properties (3 retries at 5 second intervals) can be overridden a| image::images/tickmark.png[] a| image::images/tickmark.png[] -| possibleAuthenticationFailureFatal +| possibleAuthentication +FailureFatal (possible-authentication-failure-fatal) a| When set to `true` (default), if a `PossibleAuthenticationFailureException` is thrown during connection, it is considered fatal. @@ -4587,7 +4588,10 @@ You can also use a properties bean to set the property globally for all containe [source,xml] ---- - false + + false + ---- @@ -4750,7 +4754,8 @@ a| image::images/tickmark.png[] a| image::images/tickmark.png[] a| image::images/tickmark.png[] -| afterReceivePostProcessors +| afterReceive +PostProcessors (N/A) | An array of `MessagePostProcessor` s which are invoked, before invoking the listener. @@ -4955,7 +4960,8 @@ The `deliveryMode` (or any other properties) can be changed in the `additionalHe RepublishMessageRecoverer recoverer = new RepublishMessageRecoverer(amqpTemplate, "error") { protected Map additionalHeaders(Message message, Throwable cause) { - message.getMessageProperties().setDeliveryMode(message.getMessageProperties().getReceivedDeliveryMode()); + message.getMessageProperties() + .setDeliveryMode(message.getMessageProperties().getReceivedDeliveryMode()); return null; } diff --git a/src/reference/asciidoc/testing.adoc b/src/reference/asciidoc/testing.adoc index 8b703375..c14aae1e 100644 --- a/src/reference/asciidoc/testing.adoc +++ b/src/reference/asciidoc/testing.adoc @@ -278,112 +278,113 @@ Here is a simple test case that uses the template: @RunWith(SpringRunner.class) public class TestRabbitTemplateTests { - @Autowired - private TestRabbitTemplate template; + @Autowired + private TestRabbitTemplate template; - @Autowired - private Config config; + @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")); + @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")); - } + 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")); - } + @Test + public void testSendAndReceive() { + assertThat(this.template.convertSendAndReceive("baz", "hello"), equalTo("baz:hello")); + } ---- [source, java] ---- - @Configuration - @EnableRabbit - public static class Config { + @Configuration + @EnableRabbit + public static class Config { - public String fooIn = ""; + public String fooIn = ""; - public String barIn = ""; + public String barIn = ""; - public String smlc1In = "smlc1:"; + public String smlc1In = "smlc1:"; - @Bean - public TestRabbitTemplate template() throws IOException { - return new TestRabbitTemplate(connectionFactory()); - } + @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 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; - } + @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 = "foo") + public void foo(String in) { + this.fooIn += "foo:" + in; + } - @RabbitListener(queues = "bar") - public void bar(String in) { - this.barIn += "bar:" + in; - } + @RabbitListener(queues = "bar") + public void bar(String in) { + this.barIn += "bar:" + in; + } - @RabbitListener(queues = "baz") - public String baz(String in) { - return "baz:" + 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() { + @Bean + public SimpleMessageListenerContainer smlc1() throws IOException { + SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(connectionFactory()); + container.setQueueNames("foo", "bar"); + container.setMessageListener(new MessageListenerAdapter(new Object() { - @SuppressWarnings("unused") - public void handleMessage(String in) { - smlc1In += in; - } + @SuppressWarnings("unused") + public void handleMessage(String in) { + smlc1In += in; + } - })); - return container; - } + })); + return container; + } - } + } } ---- -[[junit-rules]] -==== JUnit @Rules +[[junit4-rules]] +==== JUnit4 @Rules -Spring AMQP _version 1.7_ provides an additional jar `spring-rabbit-junit`; this jar contains a couple of utility `@Rule` s for use when running JUnit tests. +Spring AMQP _version 1.7_ and later provide an additional jar `spring-rabbit-junit`; this jar contains a couple of utility `@Rule` s for use when running JUnit4 tests. +See <> for JUnit5 testing. ===== BrokerRunning @@ -407,6 +408,7 @@ public static void tearDown() { There are several `isRunning...` static methods such as `isBrokerAndManagementRunning()` which verifies the broker has the management plugin enabled. +[[brokerRunning-configure]] ====== Configuring the Rule There are times when you want tests to fail if there is no broker, such as a nightly CI build. @@ -485,3 +487,159 @@ public LongRunningIntegrationTest longTests = new LongRunningIntegrationTest(); ---- To disable the rule at runtime, set an environment variable `RUN_LONG_INTEGRATION_TESTS` to `true`. + +[[junit5-conditions]] +==== JUnit5 Conditions + +Version _2.0.2_ introduced support for JUnit5. + +===== @RabbitAvailable Annotation + +This class-level annotation is similar to the `BrokerRunning` `@Rule` discussed in <>; it is processed by the `RabbitAvailableCondition`. + +The annotation has two properties: + +* `queues` - an array of queues that will be declared (and purged) before each test and deleted when all tests are complete. +* `management` - set to `true` if your tests also require the management plugin installed on the broker. + +It is used to check if the broker is available and skip the tests if not. +As discussed in <> the environment variable `RABBITMQ_SERVER_REQUIRED`, if `true` will cause the tests to fail fast if there is no broker. +The condition can be configured using environment variables as discussed in <>. + +In addition, the `RabbitAvailableCondition` supports argument resolution for parameterized test constructors and methods. +Two argument types are supported `BrokerRunning` (the instance) and `ConnectionFactory` - the `BrokerRunning` 's RabbitMQ connection factory. + +Here is an example of each: + +[source, java] +---- +@RabbitAvailable(queues = "rabbitAvailableTests.queue") +public class RabbitAvailableCTORInjectionTests { + + private final ConnectionFactory connectionFactory; + + public RabbitAvailableCTORInjectionTests(BrokerRunning 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(); + } + +} +---- + +This 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: + +[source, java] +---- +@RabbitAvailable(queues = "rabbitAvailableTests.queue") +public class RabbitAvailableCTORInjectionTests { + + private final CachingConnectionFactory connectionFactory; + + public RabbitAvailableCTORInjectionTests(BrokerRunning brokerRunning) { + this.connectionFactory = + new CachingConnectionFactory(brokerRunning.getConnectionFactory()); + } + + @Test + public void test() throws Exception { + RabbitTemplate template = new RabbitTemplate(this.connectionFactory); + ... + } +} +---- + +When using a Spring annotation application context within a test class, it is also possible to get a reference to the condition's connection factory via a static method `RabbitAvailableCondition.getBrokerRunning()`. +Here is another test from the framework that demonstrates the usage: + +[source, java] +---- +@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; + } + + } + +} +---- + +===== @LongRunning Annotation + +Similar to the `LongRunningIntegrationTest` JUnit4 `@Rule`, this annotation causes tests to be skipped unless an environment variable (or system property) is set to `true`. + +[source, java] +---- +@RabbitAvailable(queues = SimpleMessageListenerContainerLongTests.QUEUE) +@LongRunning +public class SimpleMessageListenerContainerLongTests { + + public static final String QUEUE = "SimpleMessageListenerContainerLongTests.queue"; + +... + +} +---- + +By default, the variable is `RUN_LONG_INTEGRATION_TESTS` but the variable name can be specified in the annotation's `value` attribute.