Search code examples
javajunitjmsopenejbmessage-driven-bean

How to build a auto reply JMS listener in JUnit (in OpenEJB)


I have a EJB to send a message to JMS queue and wait the reply from it. I want to test the EJB, it's easy to use OpenEJB to do the JUnit test of the EJB. But the problem is this EJB will wait the JMS response to continue process.

Although I can send message in my junit code, but because the EJB is still on-going, I cannot run it before the EJB is completed.

2nd solution is I can initialize a MDB to listen and reply the JMS message form the EJB, but the problem is the MDB must in src\main\java and cannot in src\test\java. The problem is this is just a test code and I should not package it to production environment. (I use Maven)

Or should I use mock object ?


Solution

  • You're on the right track. There area few ways to handle this. Here are a couple tips for unit testing with OpenEJB and Maven.

    Test beans

    You can write all sorts of EJBs and other testing utilities and have them deployed. All you need is a ejb-jar.xml for the test code like so:

    • src/main/resources/ejb-jar.xml (the normal one)

    • src/test/resources/ejb-jar.xml (the testing beans)

    As usual the ejb-jar.xml file only needs to contain <ejb-jar/> and nothing more. Its existence simply tells OpenEJB to inspect that part of the classpath and scan it for beans. Scanning the entire classpath is very slow, so this is just convention to speed that up.

    TestCase injection

    With the above src/test/resources/ejb-jar.xml you could very easily add that test-only MDB and have it setup to process the request in a way that the TestCase needs. But the src/test/resources/ejb-jar.xml also opens up some other interesting functionality.

    You could have the TestCase itself do it by declaring references to whatever JMS resources you need and have them injected.

    import org.apache.openejb.api.LocalClient;
    
    @LocalClient
    public class ChatBeanTest extends TestCase {
    
        @Resource
        private ConnectionFactory connectionFactory;
    
        @Resource(name = "QuestionBean")
        private Queue questionQueue;
    
        @Resource(name = "AnswerQueue")
        private Queue answerQueue;
    
        @EJB
        private MyBean myBean;
    
    
        @Override
        protected void setUp() throws Exception {
            Properties p = new Properties();
            p.put(Context.INITIAL_CONTEXT_FACTORY, "org.apache.openejb.client.LocalInitialContextFactory");
            InitialContext initialContext = new InitialContext(p);
    
            initialContext.bind("inject", this); // here's the magic!
        }
    }
    

    Now you're just one thread away from being able to respond to the JMS message the testcase itself. You can launch off a little runnable that will read a single message, send the response you want, then exit.

    Maybe something like:

    public void test() throws Exception {
    
        final Thread thread = new Thread() {
            @Override
            public void run() {
                try {
                    final Connection connection = connectionFactory.createConnection();
    
                    connection.start();
    
                    final Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
    
                    final MessageConsumer incoming = session.createConsumer(requestQueue);
                    final String text = ((TextMessage) incoming.receive(1000)).getText();
    
                    final MessageProducer outgoing = session.createProducer(responseQueue);
                    outgoing.send(session.createTextMessage("Hello World!"));
    
                } catch (JMSException e) {
                    e.printStackTrace();
                }
            }
        };
        thread.setDaemon(true);
        thread.start();
    
        myBean.doThatThing();
    
        // asserts here...
    }
    

    See

    Alternate Descriptors

    If you did want to use the MDB solution and only wanted to enable it for just the one test and not all tests, you could define it in a special src/test/resources/mockmdb.ejb-jar.xml file and enable it in the specific test case(s) where it is needed.

    See this doc for more information on how to enable that descriptor and the various options of alternate descriptors.