Skip to content

Commit

Permalink
AMQP-791: Support JUnit5
Browse files Browse the repository at this point in the history
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
  • Loading branch information
garyrussell authored and artembilan committed Dec 20, 2017
1 parent 48412b9 commit d73f4ff
Show file tree
Hide file tree
Showing 15 changed files with 840 additions and 184 deletions.
27 changes: 23 additions & 4 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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'
}
}

Expand Down Expand Up @@ -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'
Expand All @@ -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"
Expand All @@ -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
Expand Down Expand Up @@ -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'

}

Expand All @@ -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"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
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;
Expand Down Expand Up @@ -367,38 +369,8 @@ public Statement apply(Statement base, Description description) {
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());
Expand All @@ -419,7 +391,59 @@ public Statement apply(Statement base, Description description) {
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");
Expand Down Expand Up @@ -465,7 +489,7 @@ public void removeTestQueues(String... additionalQueues) {
Channel channel = null;

try {
connection = connectionFactory.newConnection();
connection = getConnection(connectionFactory);
connection.setId(generateId() + ".queueDelete");
channel = connection.createChannel();

Expand All @@ -491,7 +515,7 @@ public void deleteQueues(String... queues) {
Channel channel = null;

try {
connection = connectionFactory.newConnection();
connection = getConnection(connectionFactory);
connection.setId(generateId() + ".queueDelete");
channel = connection.createChannel();

Expand All @@ -517,7 +541,7 @@ public void deleteExchanges(String... exchanges) {
Channel channel = null;

try {
connection = connectionFactory.newConnection();
connection = getConnection(connectionFactory);
connection.setId(generateId() + ".exchangeDelete");
channel = connection.createChannel();

Expand Down
Original file line number Diff line number Diff line change
@@ -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;

}
Original file line number Diff line number Diff line change
@@ -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.
Expand Down Expand Up @@ -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;
Expand All @@ -58,4 +67,12 @@ public Statement apply(Statement base, Description description) {
return super.apply(base, description);
}

/**
* Return true if the test should run.
* @return true to run.
*/
public boolean isShouldRun() {
return this.shouldRun;
}

}
Original file line number Diff line number Diff line change
@@ -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<AnnotatedElement> element = context.getElement();
Optional<LongRunning> 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;
}

}
Loading

0 comments on commit d73f4ff

Please sign in to comment.