■ 11.13—Test the content of your JNDI directory
■ Self-Shunt pattern
(www.objectmentor.com/resources/articles/SelfShunPtrn.pdf)
11.11 Test JMS message-processing logic
◆ Problem
You want to test the logic for processing a JMS message without worrying about how that message is delivered.
◆ Background
The JMS API is complicated, or at least, verbose. It takes several lines of code just to send a simple text message using a live JMS server. If you try to mock all the objects involved, a single test can run between 50 and 100 lines, depending on your code formatter—all that just to test the business logic triggered by a specific kind of mes- sage. When discussing how to test J2EE applications we have emphasized the importance of testing business logic entirely in isolation from J2EE components.
TE AM FL Y
Team-Fly®
431 Test JMS message-processing logic
This affords you maximum flexibility in your design as well as making it much eas- ier to test the most important part of your application: how well it solves your busi- ness problem. For that reason, it is important to test your business logic separately—the logic that you plan to execute when you receive a particular JMS message. This recipe describes how to do that.
◆ Recipe
In recipe 11.8 we describe the design we recommend: the EJB container delivers the message to a message-driven bean, which performs JNDI lookups, and then passes the message to a JMS message consumer, which unmarshals the message and passes it to a message processor. This recipe focuses on the message proces- sor. There is one key principle to make these tests simple: the message processor should have no knowledge of JMS or messaging. If you can refactor towards that design, then you can treat your message processor like any other Plain Old Java Object and test it in a straightforward manner.
Returning to our example of receiving and processing an order, we want to send e-mail to the customer saying that we have received her order. Fortunately, we have already isolated the e-mail feature into an interface named MailService. (See recipe 11.7 for a description of when and why we created this interface.) This simplifies the corresponding tests. The test in listing 11.15 verifies that we used the correct (customer’s) e-mail address for the “To” address in our “We received your order” e-mail.
package junit.cookbook.coffee.model.logic.test;
import junit.cookbook.coffee.model.logic.ProcessOrderSubmissionAction;
import junit.cookbook.coffee.service.MailService;
import junit.framework.TestCase;
public class ProcessOrderSubmissionActionTest extends TestCase { private boolean spyMailServiceInvoked = false;
public void testToAddress() throws Exception { ProcessOrderSubmissionAction action = new ProcessOrderSubmissionAction();
MailService spyMailService = new MailService() { public void sendMessage(
String fromAddress, String toAddress, String subject, String bodyText) {
Listing 11.15 Verifying the “To” address in an e-mail
432 CHAPTER 11
Testing Enterprise JavaBeans
assertEquals("jbr@diasparsoftware.com", toAddress);
spyMailServiceInvoked = true;
} };
action.processOrder(spyMailService, "jbr@diasparsoftware.com");
assertTrue(spyMailServiceInvoked);
} }
Here we decided to use a hand-coded Spy implementation of MailService, rather than an all-out EasyMock mock object. This test is only concerned with whether we get the “To” address right; we will write other tests to verify the content of the e-mail and what happens if MailService.sendMessage() throws an exception. For this test, however, the simplest approach is the one you see here. We need to check a flag representing “the Spy mail service was invoked” to avoid a false posi- tive29 in the case where nothing invoked sendMessage(). If it did not invoke our Spy mail service’s method, then nothing would execute its assertion either.
The key point to note is that these tests have nothing to do with EJBs, JMS, or any- thing else. We are merely testing a Plain Old Java Object—the easiest tests to write.
◆ Discussion
This test is very simple—almost too simple. Fear not: it is this way on purpose. The idea is to test the action, and not the objects with which the action collaborates!
Elsewhere we have tested our production implementation of MailService, the one that uses JavaMail to send e-mail. We do not even need to concern ourselves with which MailService implementation is passed into our action class—that is up to the object that invokes this action, and we will test that too. (See the JMS message consumer and message-driven bean recipes in this chapter for examples.) The sim- plicity of the test comes from the simplicity of the action, and that is good design.
One note on the test itself: because MailService is an interface, we could have used the Self-Shunt pattern and had the test case implement MailService itself, rather than use an anonymous implementation bound to the test method. If many tests want to use the same Spy MailService implementation, then we recom- mend moving those tests into a separate fixture and using the Self-Shunt pattern.
This removes duplication from the tests.
29That is, a test that passes even though the production code does not behave correctly. Such tests are very bad for you: they give you a false sense of progress, and problems jump up at you unexpectedly, and much later on.
433 Test a JMS message producer
◆ Related
■ 11.7—Test a message-driven bean inside the container
■ 11.8—Test a message-driven bean outside the container