ARTEMIS-737 Improving Tests by checking thread leaks and Waiting condition, also adding docs

This commit is contained in:
Clebert Suconic 2016-09-26 15:33:39 -04:00
parent 32b7c039d8
commit 03b3b9fa80
19 changed files with 437 additions and 17 deletions

View File

@ -31,7 +31,7 @@ import org.apache.activemq.artemis.api.core.client.ServerLocator;
* <pre><code>
* public class SimpleTest {
* {@code @Rule}
* public ActiveMQConsumerResource producer = new ActiveMQProducerResource( "vm://0", "test.queue" );
* public ActiveMQConsumerResource client = new ActiveMQProducerResource( "vm://0", "test.queue" );
*
* {@code @Test}
* public void testSomething() throws Exception {

View File

@ -0,0 +1,223 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.activemq.artemis.junit;
import java.lang.ref.WeakReference;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import org.apache.activemq.artemis.api.core.client.ActiveMQClient;
import org.apache.activemq.artemis.core.remoting.impl.invm.InVMConnector;
import org.jboss.logging.Logger;
import org.junit.Assert;
import org.junit.rules.ExternalResource;
/**
* Messaging tests are usually Thread intensive and a thread leak or a server leakage may affect future tests.
* This Rule will prevent Threads leaking from one test into another by checking left over threads.
* This will also clear Client Thread Pools from ActiveMQClient.
*/
public class ThreadLeakCheckRule extends ExternalResource {
private static Logger log = Logger.getLogger(ThreadLeakCheckRule.class);
private static Set<String> knownThreads = new HashSet<>();
boolean enabled = true;
private Map<Thread, StackTraceElement[]> previousThreads;
public void disable() {
enabled = false;
}
/**
* Override to set up your specific external resource.
*
* @throws if setup fails (which will disable {@code after}
*/
@Override
protected void before() throws Throwable {
// do nothing
previousThreads = Thread.getAllStackTraces();
}
/**
* Override to tear down your specific external resource.
*/
@Override
protected void after() {
ActiveMQClient.clearThreadPools();
InVMConnector.resetThreadPool();
try {
if (enabled) {
boolean failed = true;
boolean failedOnce = false;
long timeout = System.currentTimeMillis() + 60000;
while (failed && timeout > System.currentTimeMillis()) {
failed = checkThread();
if (failed) {
failedOnce = true;
forceGC();
try {
Thread.sleep(500);
}
catch (Throwable e) {
}
}
}
if (failed) {
Assert.fail("Thread leaked");
}
else if (failedOnce) {
System.out.println("******************** Threads cleared after retries ********************");
System.out.println();
}
}
else {
enabled = true;
}
}
finally {
// clearing just to help GC
previousThreads = null;
}
}
private static int failedGCCalls = 0;
public static void forceGC() {
if (failedGCCalls >= 10) {
log.info("ignoring forceGC call since it seems System.gc is not working anyways");
return;
}
log.info("#test forceGC");
CountDownLatch finalized = new CountDownLatch(1);
WeakReference<DumbReference> dumbReference = new WeakReference<>(new DumbReference(finalized));
long timeout = System.currentTimeMillis() + 1000;
// A loop that will wait GC, using the minimal time as possible
while (!(dumbReference.get() == null && finalized.getCount() == 0) && System.currentTimeMillis() < timeout) {
System.gc();
System.runFinalization();
try {
finalized.await(100, TimeUnit.MILLISECONDS);
}
catch (InterruptedException e) {
}
}
if (dumbReference.get() != null) {
failedGCCalls++;
log.info("It seems that GC is disabled at your VM");
}
else {
// a success would reset the count
failedGCCalls = 0;
}
log.info("#test forceGC Done ");
}
public static void removeKownThread(String name) {
knownThreads.remove(name);
}
public static void addKownThread(String name) {
knownThreads.add(name);
}
private boolean checkThread() {
boolean failedThread = false;
Map<Thread, StackTraceElement[]> postThreads = Thread.getAllStackTraces();
if (postThreads != null && previousThreads != null && postThreads.size() > previousThreads.size()) {
for (Thread aliveThread : postThreads.keySet()) {
if (aliveThread.isAlive() && !isExpectedThread(aliveThread) && !previousThreads.containsKey(aliveThread)) {
if (!failedThread) {
System.out.println("*********************************************************************************");
System.out.println("LEAKING THREADS");
}
failedThread = true;
System.out.println("=============================================================================");
System.out.println("Thread " + aliveThread + " is still alive with the following stackTrace:");
StackTraceElement[] elements = postThreads.get(aliveThread);
for (StackTraceElement el : elements) {
System.out.println(el);
}
}
}
if (failedThread) {
System.out.println("*********************************************************************************");
}
}
return failedThread;
}
/**
* if it's an expected thread... we will just move along ignoring it
*
* @param thread
* @return
*/
private boolean isExpectedThread(Thread thread) {
for (String known: knownThreads) {
if (thread.getName().contains(known)) {
return true;
}
}
return false;
}
protected static class DumbReference {
private CountDownLatch finalized;
public DumbReference(CountDownLatch finalized) {
this.finalized = finalized;
}
@Override
public void finalize() throws Throwable {
finalized.countDown();
super.finalize();
}
}
}

View File

@ -0,0 +1,55 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.activemq.artemis.junit;
import java.util.concurrent.TimeUnit;
/**
* Utility adapted from: org.apache.activemq.util.Wait
*/
public class Wait {
public static final long MAX_WAIT_MILLIS = 30 * 1000;
public static final int SLEEP_MILLIS = 1000;
public interface Condition {
boolean isSatisfied() throws Exception;
}
public static boolean waitFor(Condition condition) throws Exception {
return waitFor(condition, MAX_WAIT_MILLIS);
}
public static boolean waitFor(final Condition condition, final long duration) throws Exception {
return waitFor(condition, duration, SLEEP_MILLIS);
}
public static boolean waitFor(final Condition condition,
final long duration,
final long sleepMillis) throws Exception {
final long expiry = System.currentTimeMillis() + duration;
boolean conditionSatisified = condition.isSatisfied();
while (!conditionSatisified && System.currentTimeMillis() < expiry) {
TimeUnit.MILLISECONDS.sleep(sleepMillis);
conditionSatisified = condition.isSatisfied();
}
return conditionSatisified;
}
}

View File

@ -50,14 +50,22 @@ public class ActiveMQConsumerResourceTest {
ActiveMQConsumerResource consumer = new ActiveMQConsumerResource(server.getVmURL(), TEST_QUEUE);
@Rule
public RuleChain ruleChain = RuleChain.outerRule(server).around(consumer);
public RuleChain ruleChain = RuleChain.outerRule(new ThreadLeakCheckRule()).outerRule(server).around(consumer);
ClientMessage sent = null;
@After
public void tearDown() throws Exception {
assertNotNull(String.format(ASSERT_SENT_FORMAT, TEST_ADDRESS), sent);
Wait.waitFor(new Wait.Condition() {
@Override
public boolean isSatisfied() throws Exception {
return server.getMessageCount("TEST_QUEUE") == 1;
}
}, 5000, 100);
assertEquals(String.format(ASSERT_COUNT_FORMAT, TEST_QUEUE), 1, server.getMessageCount(TEST_QUEUE));
ClientMessage received = consumer.receiveMessage();

View File

@ -51,7 +51,7 @@ public class ActiveMQDynamicProducerResourceTest {
ActiveMQDynamicProducerResource producer = new ActiveMQDynamicProducerResource(server.getVmURL(), TEST_QUEUE_ONE);
@Rule
public RuleChain ruleChain = RuleChain.outerRule(server).around(producer);
public RuleChain ruleChain = RuleChain.outerRule(new ThreadLeakCheckRule()).around(server).around(producer);
ClientMessage sentOne = null;
ClientMessage sentTwo = null;
@ -60,6 +60,12 @@ public class ActiveMQDynamicProducerResourceTest {
public void tearDown() throws Exception {
assertNotNull(String.format(ASSERT_SENT_FORMAT, TEST_QUEUE_ONE), sentOne);
assertNotNull(String.format(ASSERT_SENT_FORMAT, TEST_QUEUE_TWO), sentTwo);
Wait.waitFor(new Wait.Condition() {
@Override
public boolean isSatisfied() throws Exception {
return server.getMessageCount(TEST_QUEUE_ONE) == 1 && server.getMessageCount(TEST_QUEUE_TWO) == 1;
}
}, 5000, 100);
assertEquals(String.format(ASSERT_COUNT_FORMAT, TEST_QUEUE_ONE), 1, server.getMessageCount(TEST_QUEUE_ONE));
assertEquals(String.format(ASSERT_COUNT_FORMAT, TEST_QUEUE_TWO), 1, server.getMessageCount(TEST_QUEUE_TWO));

View File

@ -43,7 +43,7 @@ public class ActiveMQDynamicProducerResourceWithoutAddressExceptionTest {
ActiveMQDynamicProducerResource producer = new ActiveMQDynamicProducerResource(server.getVmURL());
@Rule
public RuleChain ruleChain = RuleChain.outerRule(server).around(producer);
public RuleChain ruleChain = RuleChain.outerRule(new ThreadLeakCheckRule()).around(server).around(producer);
ClientMessage sentOne = null;

View File

@ -52,7 +52,7 @@ public class ActiveMQDynamicProducerResourceWithoutAddressTest {
ActiveMQDynamicProducerResource producer = new ActiveMQDynamicProducerResource(server.getVmURL());
@Rule
public RuleChain ruleChain = RuleChain.outerRule(server).around(producer);
public RuleChain ruleChain = RuleChain.outerRule(new ThreadLeakCheckRule()).around(server).around(producer);
ClientMessage sentOne = null;
ClientMessage sentTwo = null;
@ -68,6 +68,12 @@ public class ActiveMQDynamicProducerResourceWithoutAddressTest {
public void tearDown() throws Exception {
assertNotNull(String.format(ASSERT_SENT_FORMAT, TEST_QUEUE_ONE), sentOne);
assertNotNull(String.format(ASSERT_SENT_FORMAT, TEST_QUEUE_TWO), sentTwo);
Wait.waitFor(new Wait.Condition() {
@Override
public boolean isSatisfied() throws Exception {
return server.getMessageCount(TEST_QUEUE_ONE) == 1 && server.getMessageCount(TEST_QUEUE_TWO) == 1;
}
}, 5000, 100);
assertEquals(String.format(ASSERT_COUNT_FORMAT, TEST_QUEUE_ONE), 1, server.getMessageCount(TEST_QUEUE_ONE));
assertEquals(String.format(ASSERT_COUNT_FORMAT, TEST_QUEUE_TWO), 1, server.getMessageCount(TEST_QUEUE_TWO));

View File

@ -50,7 +50,7 @@ public class ActiveMQProducerResourceTest {
ActiveMQProducerResource producer = new ActiveMQProducerResource(server.getVmURL(), TEST_ADDRESS);
@Rule
public RuleChain ruleChain = RuleChain.outerRule(server).around(producer);
public RuleChain ruleChain = RuleChain.outerRule(new ThreadLeakCheckRule()).around(server).around(producer);
ClientMessage sent = null;

View File

@ -24,6 +24,7 @@ import org.apache.activemq.artemis.core.config.impl.ConfigurationImpl;
import org.apache.activemq.artemis.core.server.Queue;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.RuleChain;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@ -37,8 +38,10 @@ public class EmbeddedActiveMQResourceCustomConfigurationTest {
CoreQueueConfiguration queueConfiguration = new CoreQueueConfiguration().setAddress(TEST_ADDRESS).setName(TEST_QUEUE);
Configuration customConfiguration = new ConfigurationImpl().setPersistenceEnabled(false).setSecurityEnabled(true).addQueueConfiguration(queueConfiguration);
private EmbeddedActiveMQResource server = new EmbeddedActiveMQResource(customConfiguration);
@Rule
public EmbeddedActiveMQResource server = new EmbeddedActiveMQResource(customConfiguration);
public RuleChain rulechain = RuleChain.outerRule(new ThreadLeakCheckRule()).around(server);
@Test
public void testCustomConfiguration() throws Exception {

View File

@ -21,6 +21,7 @@ import java.util.List;
import org.apache.activemq.artemis.core.server.Queue;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.RuleChain;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
@ -31,8 +32,11 @@ public class EmbeddedActiveMQResourceFileConfigurationTest {
static final String TEST_QUEUE = "test.queue";
static final String TEST_ADDRESS = "test.address";
private EmbeddedActiveMQResource server = new EmbeddedActiveMQResource("embedded-artemis-server.xml");
@Rule
public EmbeddedActiveMQResource server = new EmbeddedActiveMQResource("embedded-artemis-server.xml");
public RuleChain rulechain = RuleChain.outerRule(new ThreadLeakCheckRule()).around(server);
@Test
public void testConfiguredQueue() throws Exception {

View File

@ -25,6 +25,7 @@ import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.RuleChain;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
@ -46,9 +47,11 @@ public class EmbeddedActiveMQResourceTest {
TEST_PROPERTIES.put("PropertyTwo", "Property Value 2");
}
@Rule
public EmbeddedActiveMQResource server = new EmbeddedActiveMQResource();
@Rule
public RuleChain rulechain = RuleChain.outerRule(new ThreadLeakCheckRule()).around(server);
ClientMessage sent = null;
@Before
@ -59,6 +62,12 @@ public class EmbeddedActiveMQResourceTest {
@After
public void tearDown() throws Exception {
assertNotNull(String.format(ASSERT_SENT_FORMAT, TEST_ADDRESS), sent);
Wait.waitFor(new Wait.Condition() {
@Override
public boolean isSatisfied() throws Exception {
return server.getMessageCount(TEST_QUEUE) == 1;
}
}, 5000, 100);
assertEquals(String.format(ASSERT_COUNT_FORMAT, TEST_QUEUE), 1, server.getMessageCount(TEST_QUEUE));
ClientMessage received = server.receiveMessage(TEST_QUEUE);

View File

@ -29,6 +29,7 @@ import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.RuleChain;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
@ -42,9 +43,11 @@ public class EmbeddedJMSResourceMultipleFileConfigurationTest {
static final String ASSERT_PUSHED_FORMAT = "Message should have been pushed a message to %s";
static final String ASSERT_COUNT_FORMAT = "Unexpected message count in destination %s";
@Rule
public EmbeddedJMSResource jmsServer = new EmbeddedJMSResource("embedded-artemis-minimal-server.xml", "embedded-artemis-jms-only.xml");
@Rule
public RuleChain rulechain = RuleChain.outerRule(new ThreadLeakCheckRule()).around(jmsServer);
ConnectionFactory connectionFactory;
Connection connection;
Session session;

View File

@ -24,6 +24,7 @@ import java.util.Map;
import org.junit.After;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.RuleChain;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
@ -48,14 +49,22 @@ public class EmbeddedJMSResourceQueueTest {
TEST_PROPERTIES.put("PropertyTwo", "Property Value 2");
}
@Rule
public EmbeddedJMSResource jmsServer = new EmbeddedJMSResource();
@Rule
public RuleChain rulechain = RuleChain.outerRule(new ThreadLeakCheckRule()).around(jmsServer);
Message pushed = null;
@After
public void tearDown() throws Exception {
assertNotNull(String.format(ASSERT_PUSHED_FORMAT, TEST_DESTINATION_NAME), pushed);
Wait.waitFor(new Wait.Condition() {
@Override
public boolean isSatisfied() throws Exception {
return jmsServer.getMessageCount(TEST_DESTINATION_NAME) == 1;
}
}, 5000, 100);
assertEquals(String.format(ASSERT_COUNT_FORMAT, TEST_DESTINATION_NAME), 1, jmsServer.getMessageCount(TEST_DESTINATION_NAME));
}

View File

@ -29,6 +29,7 @@ import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.RuleChain;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
@ -42,9 +43,11 @@ public class EmbeddedJMSResourceSingleFileConfigurationTest {
static final String ASSERT_PUSHED_FORMAT = "Message should have been pushed a message to %s";
static final String ASSERT_COUNT_FORMAT = "Unexpected message count in destination %s";
@Rule
public EmbeddedJMSResource jmsServer = new EmbeddedJMSResource("embedded-artemis-jms-server.xml");
@Rule
public RuleChain rulechain = RuleChain.outerRule(new ThreadLeakCheckRule()).around(jmsServer);
ConnectionFactory connectionFactory;
Connection connection;
Session session;

View File

@ -31,6 +31,7 @@ import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.RuleChain;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
@ -55,9 +56,11 @@ public class EmbeddedJMSResourceTopicTest {
TEST_PROPERTIES.put("PropertyTwo", "Property Value 2");
}
@Rule
public EmbeddedJMSResource jmsServer = new EmbeddedJMSResource();
@Rule
public RuleChain rulechain = RuleChain.outerRule(new ThreadLeakCheckRule()).around(jmsServer);
Message pushed = null;
ConnectionFactory connectionFactory;
@ -77,6 +80,12 @@ public class EmbeddedJMSResourceTopicTest {
@After
public void tearDown() throws Exception {
assertNotNull(String.format(ASSERT_PUSHED_FORMAT, TEST_DESTINATION_NAME), pushed);
Wait.waitFor(new Wait.Condition() {
@Override
public boolean isSatisfied() throws Exception {
return jmsServer.getMessageCount(TEST_DESTINATION_NAME) == 1;
}
}, 5000, 100);
assertEquals(String.format(ASSERT_COUNT_FORMAT, TEST_DESTINATION_NAME), 1, jmsServer.getMessageCount(TEST_DESTINATION_NAME));
consumer.close();

View File

@ -21,6 +21,7 @@ import org.apache.activemq.artemis.api.core.client.ClientMessage;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.RuleChain;
import static org.junit.Assert.assertNotNull;
@ -37,12 +38,13 @@ public class MultipleEmbeddedActiveMQResourcesTest {
static final String ASSERT_RECEIVED_FORMAT = "Message should have been received from %s";
static final String ASSERT_COUNT_FORMAT = "Unexpected message count in queue %s";
@Rule
public EmbeddedActiveMQResource serverOne = new EmbeddedActiveMQResource(0);
@Rule
public EmbeddedActiveMQResource serverTwo = new EmbeddedActiveMQResource(1);
@Rule
public RuleChain rulechain = RuleChain.outerRule(new ThreadLeakCheckRule()).around(serverOne).around(serverTwo);
@Before
public void setUp() throws Exception {
serverOne.createQueue(TEST_ADDRESS_ONE, TEST_QUEUE_ONE);

View File

@ -20,6 +20,7 @@ import javax.jms.Message;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.RuleChain;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
@ -33,12 +34,13 @@ public class MultipleEmbeddedJMSResourcesTest {
static final String ASSERT_PUSHED_FORMAT = "Message should have been pushed a message to %s";
static final String ASSERT_COUNT_FORMAT = "Unexpected message count in destination %s";
@Rule
public EmbeddedJMSResource jmsServerOne = new EmbeddedJMSResource(0);
@Rule
public EmbeddedJMSResource jmsServerTwo = new EmbeddedJMSResource(1);
@Rule
public RuleChain rulechain = RuleChain.outerRule(new ThreadLeakCheckRule()).around(jmsServerOne).around(jmsServerTwo);
@Test
public void testMultipleServers() throws Exception {
Message pushedOne = jmsServerOne.pushMessage(TEST_QUEUE_ONE, TEST_BODY);

View File

@ -56,6 +56,7 @@
* [Protocols and Interoperability](protocols-interoperability.md)
* [Tools](tools.md)
* [Maven Plugin](maven-plugin.md)
* [Unit Testing](unit-testing.md)
* [Troubleshooting and Performance Tuning](perf-tuning.md)
* [Configuration Reference](configuration-index.md)

View File

@ -0,0 +1,77 @@
# Unit Testing
The package ```artemis-junit``` provides tools to facilitate how to run Artemis resources inside Junit Tests.
These are provided as junit rules and can make it easier to embed Messaging functionality on your tests.
## Example
### Import this on your pom.xml
```xml
<dependency>
<groupId>org.apache.activemq</groupId>
<artifactId>artemis-junit</artifactId>
<!-- replace this for the version you are using -->
<version>1.5.0</version>
<scope>test</scope>
</dependency>
```
### Declare a rule on your JUnit Test
```java
import org.apache.activemq.artemis.junit.EmbeddedJMSResource;
import org.junit.Rule;
import org.junit.Test;
public class MyTest {
@Rule
public EmbeddedJMSResource resource = new EmbeddedJMSResource();
@Test
public void myTest() {
}
}
```
This will start a server that will be available for your test:
```
ain] 17:00:16,644 INFO [org.apache.activemq.artemis.core.server] AMQ221000: live Message Broker is starting with configuration Broker Configuration (clustered=false,journalDirectory=data/journal,bindingsDirectory=data/bindings,largeMessagesDirectory=data/largemessages,pagingDirectory=data/paging)
[main] 17:00:16,666 INFO [org.apache.activemq.artemis.core.server] AMQ221045: libaio is not available, switching the configuration into NIO
[main] 17:00:16,688 INFO [org.apache.activemq.artemis.core.server] AMQ221043: Protocol module found: [artemis-server]. Adding protocol support for: CORE
[main] 17:00:16,801 INFO [org.apache.activemq.artemis.core.server] AMQ221007: Server is now live
[main] 17:00:16,801 INFO [org.apache.activemq.artemis.core.server] AMQ221001: Apache ActiveMQ Artemis Message Broker version 1.5.0-SNAPSHOT [embedded-jms-server, nodeID=39e78380-842c-11e6-9e43-f45c8992f3c7]
[main] 17:00:16,891 INFO [org.apache.activemq.artemis.core.server] AMQ221002: Apache ActiveMQ Artemis Message Broker version 1.5.0-SNAPSHOT [39e78380-842c-11e6-9e43-f45c8992f3c7] stopped, uptime 0.272 seconds
```
### Ordering rules
This is actually a Junit feature, but this could be helpful on pre-determining the order on which rules are executed.
```java
ActiveMQDynamicProducerResource producer = new ActiveMQDynamicProducerResource(server.getVmURL());
@Rule
public RuleChain ruleChain = RuleChain.outerRule(new ThreadLeakCheckRule()).around(server).around(producer);
```
### Available Rules
Name | Description
:--- | :---
EmbeddedActiveMQResource | It will run a Server, without the JMS manager
EmbeddedJMSResource | It will run a Server, including the JMS Manager
ActiveMQConsumerResource | It will automate the creation of a consumer
ActiveMQProducerResource | It will automate the creation of a producer
ThreadLeakCheckRule | It will check that all threads have been finished after the test is finished