How to create a JMS listener answering machine in JUnit (in OpenEJB)

I have an EJB to send a message to the JMS queue and wait for a response from it. I want to test EJB, it is easy to use OpenEJB to run JUnit EJB test. But the problem is that the EJB will wait for the JMS response to continue the process.

Although I can send messages to my unit code, but since EJB is still ongoing, I cannot start it until EJB is complete.

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

Or should I use a mock object?

+4
source share
5 answers

You are on the right track. There are several ways to handle this. Here are some tips for unit testing with OpenEJB and Maven.

Beans test

You can write all kinds of EJBs and other testing utilities and deploy them. All you need is ejb-jar.xml for the test code:

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

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

As usual, the ejb-jar.xml file should contain <ejb-jar/> and nothing else. Its existence simply tells OpenEJB to check this part of the classpath and scan it for beans. Scanning the entire classpath is very slow, so this is just an agreement to speed it up.

TestCase Insert

With src/test/resources/ejb-jar.xml above, you can easily add this test MDB and configure it to handle the request in the way that TestCase requires. But src/test/resources/ejb-jar.xml also opens up some other interesting features.

You could make TestCase yourself by declaring links to all the JMS resources you need and posting them.

 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 the magic! } } 

Now you are just one thread away from responding to the test JMS message itself. You can run a small runnable that will read a single message, send the answer you want, and 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... } 

Cm.

Alternative Descriptors

If you want to use the MDB solution and want to enable it for only one test, and not for all tests, you can define it in a special file src/test/resources/mockmdb.ejb-jar.xml and enable it in a specific test case, where necessary.

For more information on how to enable this descriptor and various alternative descriptor options, see this document .

+6
source

I think you should use mocks for this. If you send messages to a real JMS server, listen to them, reply to them, etc., then you are doing something other than unit test. I am not going to enter into the argument that this should be called, but I think it is pretty well recognized that a single test should not talk to live databases, message queues, etc.

+2
source

If I understand your question correctly, this is a bad design for EJB to send a JMS message, and then wait for an answer that actually contradicts the whole idea of โ€‹โ€‹EJB.

You send a JMS message and then forget about it. You have an MDB to receive the message. If an EJB depends on the response, JMS is not the way to go, but rather use a different EJB.

To test sending, mock JMS classes, check MDB separately.

EJBs are designed for synchronous tasks, JMS for asynchronous tasks - if you need to perform asynchronous communication with an external system, I suggest you create your system after that and execute the correct asynchronous flows. An EJB that sits and waits for a JMS response is, at best, an ugly hack and won't add anything good to your system design.

+2
source

Thank you, David answered, this is what I want. I know that unit test should not depend on other external resources, such as a JMS server. But if I use Maven + OpenEJB, I can still give test code in a closed environment. This can help do an automated test with an external resource dependency, especially for some older programs that are not easy to reorganize.

And if you see the following error message in initialContext.bind ("Paste" this)

Verify that the class was annotated with @ org.apache.openejb.api.LocalClient and was successfully discovered and deployed.

One link is http://openejb.apache.org/3.0/local-client-injection.html , but adding "openejb.tempclassloader.skip = annotations" does not work for me. Check this document OpenEJB Local Client Error . There is already a patch for it, I think it will be fixed in OpenEJB 3.1.5 or 4.0

+1
source

I also found that it is best to actually split your logic in your MDB into another class. This isolates your business logic from being in an MDB, and allows you to expose your logic in more than one way (MDB, EJB, Web Service, POJO, etc.). It also allows you to more easily test your business logic without having to test the protocol (JMS in this case).

As for JMS testing, making fun may be the best choice. Or, if you really need to test the protocol โ€œin a containerโ€, look at using something like a JBoss microcontainer (I believe you can get this in some JBoss projects like Seam). You can then launch the mini container to test things like EJB and JMS.

But in general, it is better to avoid the need for a container if absolutely necessary. That is why the excellent logic of your business from the logic of implementation (even if you are not using mocks) is good practice.

0
source

All Articles