Using JBossMQ as a mock JMS implementation
Motivation
You want to do some jms tests inside a unit test, but you don't want to configure against an external JMS broker, i.e. you want a "mock" jms.
Solution
You can do this with JBossMQ which will give you a fully functional JMS implementation with the following caveats/constraints:
No security - the security interceptor is not configured (you could configure the security, e.g. using the property file implementation of JBossSX, but you are probably not interested in security for unit tests?
No persistence - messages are not saved across restarts (using the NullPersistenceManager)
No state - durable subscriptions are not supported (using the NullStateManager)
In memory - nobody can connect externally the broker (only the In memory transport is configured)
Configuration
First we need a configuration file that bootstraps JBossMQ using the Microcontainer with the constraints from above. Here's one:
<?xml version="1.0" encoding="UTF-8"?> <deployment xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="urn:jboss:bean-deployer bean-deployer_1_0.xsd" xmlns="urn:jboss:bean-deployer"> <!-- No Persistence --> <bean name="PersistenceManager" class="org.jboss.mq.pm.none.PersistenceManager"></bean> <!-- No state/durable subscriptions --> <bean name="StateManager" class="org.jboss.mq.sm.none.NullStateManager"></bean> <!-- Message cache --> <bean name="MessageCache" class="org.jboss.mq.kernel.MessageCache"> <property name="persistenceManager"><inject bean="PersistenceManager"></inject></property> </bean> <!-- Desintation manager to control queues/topics --> <bean name="DestinationManager" class="org.jboss.mq.kernel.DestinationManager"> <property name="persistenceManagerInstance"><inject bean="PersistenceManager"></inject></property> <property name="stateManagerInstance"><inject bean="StateManager"></inject></property> <property name="messageCacheInstance"><inject bean="MessageCache"></inject></property> </bean> <!-- The server --> <bean name="JMSServer" class="org.jboss.mq.server.JMSDestinationManager"> <constructor factoryMethod="getInterceptor"> <factory bean="DestinationManager"></factory> </constructor> </bean> <!-- NOTE: OTHER INTERCEPTORS COULD GO HERE --> <!-- The invoker of the interceptor chain --> <bean name="Invoker" class="org.jboss.mq.server.JMSServerInvoker"> <property name="next"><inject bean="JMSServer"></inject></property> </bean> <!-- The in memory transport --> <bean name="ServerIL" class="org.jboss.mq.il.jvm.JVMServerIL"> <constructor> <parameter><inject bean="Invoker"></inject></parameter> </constructor> </bean> <!-- The factory that links JMS Connection factories to transports --> <bean name="GCF" class="org.jboss.mq.GenericConnectionFactory"> <constructor> <parameter><inject bean="ServerIL"></inject></parameter> <parameter> <map class="java.util.Properties" keyClass="java.lang.String" valueClass="java.lang.String"> <entry> <key>ClientILService</key><value>org.jboss.mq.il.jvm.JVMClientILService</value> </entry> </map> </parameter> </constructor> </bean> <!-- The JMS Connection factory --> <bean name="ConnectionFactory" class="org.jboss.mq.SpyConnectionFactory"> <constructor> <parameter class="org.jboss.mq.GenericConnectionFactory"><inject bean="GCF"></inject></parameter> </constructor> </bean> </deployment>
*NOTE: You could use this config to bootstrap JBossMQ using the Microcontainer generically,
not just in a unit test!*
Test Support class
Next we have the support classes that bootstrap the above configuration before running the
tests, and tears down when all the tests have been run.
It also provides methods for creating conections and creating and removing destinations.
This is the base test:
package test; import javax.jms.Connection; import javax.jms.ConnectionFactory; import org.jboss.mq.SpyDestination; import org.jboss.mq.SpyQueue; import org.jboss.mq.server.JMSDestinationManager; import org.jboss.mq.server.JMSQueue; import org.jboss.test.AbstractTestDelegate; import org.jboss.test.kernel.junit.MicrocontainerTest; public class JBossMQMicrocontainerTest extends MicrocontainerTest { /** * Get the test delegate * * @param clazz the test class * @return the delegate * @throws Exception for any error */ public static AbstractTestDelegate getDelegate(Class clazz) throws Exception { return new JBossMQMicrocontainerTestDelegate(clazz); } /** * Create a new JBossMQMicrocontainer test * * @param name the test name */ public JBossMQMicrocontainerTest(String name) { super(name); } protected ConnectionFactory getConnectionFactory() throws Exception { return (ConnectionFactory) getBean("ConnectionFactory"); } protected Connection createConnection() throws Exception { return getConnectionFactory().createConnection(); } protected SpyQueue createQueue(String name) throws Exception { SpyQueue queue = new SpyQueue(name); JMSDestinationManager server = getJMSServer(); JMSQueue realQueue = new JMSQueue(queue, null, server, server.getParameters()); server.addDestination(realQueue); return queue; } protected SpyTopic createTopic(String name) throws Exception { SpyTopic queue = new SpyTopic(name); JMSDestinationManager server = getJMSServer(); JMSTopic realTopic = new JMSTopic(topic, null, server, server.getParameters()); server.addDestination(realTopic); return topic; } protected void removeDestination(SpyDestination destination) throws Exception { JMSDestinationManager server = getJMSServer(); server.closeDestination(destination); } }
This is the helper:
package test; import org.jboss.test.kernel.junit.MicrocontainerTestDelegate; public class JBossMQMicrocontainerTestDelegate extends MicrocontainerTestDelegate { /** * Create a new JBossMQMicrocontainerTestDelegate. * * @param clazz the class * @throws Exception for any error */ public JBossMQMicrocontainerTestDelegate(Class clazz) throws Exception { super(clazz); } }
An example
This example shows how to use the above code to do normal JMS testing.
package test; import javax.jms.Connection; import javax.jms.JMSException; import javax.jms.Message; import javax.jms.MessageConsumer; import javax.jms.MessageProducer; import javax.jms.Session; import junit.framework.Test; import org.jboss.mq.SpyDestination; public class ExampleUnitTestCase extends JBossMQMicrocontainerTest { public ExampleUnitTestCase(String name) throws Exception { super(name); } public static Test suite() { return suite(ExampleUnitTestCase.class); } public void testReceiveWithWaitAfterError() throws Exception { receiveAfterError(new ReceiveOperation() { public Message receive(MessageConsumer consumer) throws JMSException { return consumer.receive(1000); } }); } public void testSomething() throws Exception { // Create the queue SpyDestination destination = createQueue("testQueue"); try { // Create the connection Connection connection = createConnection(); try { // Normal jms boiler plate Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); MessageProducer producer = session.createProducer(destination); MessageConsumer consumer = session.createConsumer(destination); connection.start(); // Send a message Message message = session.createMessage(); producer.send(message); // Receive the message and assert we got it message = consumer.receiveNoWait(); getLog.debug("Got message: " + message); assertNotNull(message); } // Always close connections in finally blocks! finally { connection.close(); } } finally { // Remove the destination removeDestination(destination); } } }
Related
JBossMQ - JBossMQ configuration
JBossMicrocontainer - The JBoss Microcontainer
Comments