Test a legacy message-driven bean

Một phần của tài liệu Manning JUnit recipes practical methods for program (Trang 453 - 457)

Problem

You want to test a message-driven bean outside the container, but do not want to refactor it (yet).

Background

Throughout this chapter we have discussed the forces that lead you to testing a legacy J2EE component as is—that is, without refactoring it towards a more test- able design. The most notable ones are fear of the unknown and creating the safety net. The first is a manifestation of the common fear, “It works and I don’t understand how, so I won’t touch it.” The second is the chicken-and-egg problem of, “I don’t know how to test it without refactoring; I ought not to refactor without tests.” A message-driven bean, being an EJB, requires an EJB container to test in its natural habitat, and we have already described the difficulties inherent in testing an EJB in a live container. The good news is that if the message-driven bean itself

423 Test a legacy message-driven bean

is simple, then the fact that it is an EJB does not make it any more difficult to test than it already is.

The complexity of the tests you need depends only on the complexity of the message-driven bean itself. Being an EJB does not make the matter worse. The EJB machinery for a message-driven bean is quite simple, but because the EJB con- tainer maintains the message-driven bean’s lifecycle, the bean has to obtain its collaborators either by instantiating them (see recipe 2.11, “Test an object that instan- tiates other objects”) or retrieving them from a JNDI directory (see recipe 11.13).

This latter issue is the source of most of the testing complexity and is the key stum- bling block to testing a message-driven bean. This recipe tells you how to cope with the EJB container-related complexity, but you need to find other recipes to help you deal with the rest. The good news is that this book is full of them.

Recipe

We recommend using MockEJB to simulate deploying your message-driven bean and to provide a mock JNDI directory. With this in place, you simply need to instantiate a Message object and invoke onMessage() directly. For most message- driven beans, this is the easy part. Returning to our order-processing example, list- ing 11.12 shows a MockEJB-based test for processing an order submission.

package junit.cookbook.coffee.model.ejb.test;

import javax.jms.*;

import javax.naming.InitialContext;

import junit.cookbook.coffee.model.ejb.ProcessOrderSubmissionBean;

import junit.cookbook.coffee.service.MailService;

import junit.framework.TestCase;

import org.mockejb.*;

import org.mockejb.jndi.*;

import com.sun.jms.MapMessageImpl;

public class ProcessOrderSubmissionLegacyBeanTest extends TestCase

implements MailService {

private MessageListener processOrderSubmissionBean;

private boolean invoked;

protected void setUp() throws Exception { invoked = false;

MockContextFactory.setAsInitial();

InitialContext rootContext = new InitialContext();

rootContext.bind("java:comp/env/service/Mail", this);

Listing 11.12 Testing order submission with MockEJB

424 CHAPTER 11

Testing Enterprise JavaBeans

MockContainer mockContainer = new MockContainer(rootContext);

MockEjbObject mockEjbObject = mockContainer.deployMessageBean(

ProcessOrderSubmissionLegacyBean.class);

processOrderSubmissionBean =

mockContainer.createMessageBean(mockEjbObject);

}

public void testHappyPath() throws Exception { final MapMessage message = new MapMessageImpl();

message.setString(

"customer-email",

"jbr@diasparsoftware.com");

processOrderSubmissionBean.onMessage(message);

assertTrue("Did not invoke MailService", invoked);

}

public void sendMessage(

String fromAddress, String toAddress, String subject, String bodyText) { invoked = true;

assertEquals("jbr@diasparsoftware.com", toAddress);

} }

This test uses MockEJB to deploy and create the message-driven bean. The test simply instantiates a MapMessage (using the standard implementation from the J2EE library, MapMessageImpl),27 adds data to it, and then invokes onMessage(). To explain the rest requires looking at the code for the message-driven bean itself.

Because onMessage()is the only interesting part, we show it in listing 11.13 and omit the rest.

package junit.cookbook.coffee.model.ejb;

import javax.ejb.*;

import javax.jms.*;

import javax.naming.*;

27Sadly, com.sun.jms.MapMessageImpl is not part of J2EE 1.4. We wanted to reuse someone else’s POJO-style implementation of MapMessage to avoid having to build our own. If you are running J2EE 1.4 then you will have to look for an alternative, or build one yourself. Sorry about that.

Listing 11.13 ProcessOrderSubmissionLegacyBean

425 Test a legacy message-driven bean

import junit.cookbook.coffee.service.MailService;

public class ProcessOrderSubmissionLegacyBean implements MessageDrivenBean, MessageListener { // Lifecycle methods omitted

public void onMessage(Message message) { try {

MapMessage incomingMessage = (MapMessage) message;

Context rootContext = new InitialContext();

Object object =

rootContext.lookup("java:comp/env/service/Mail");

MailService mailService = (MailService) object;

String customerEmailAddress =

incomingMessage.getString("customer-email");

mailService.sendMessage(

"ordering@coffeeShop.com", customerEmailAddress, "We received your order",

"Hello, there! We received your order.");

}

catch (NamingException logged) { logged.printStackTrace();

}

catch (JMSException logged) { logged.printStackTrace();

} } }

Here, onMessage() tells us that it expects a MapMessage, that it retrieves a MailSer- vice object from JNDI, and then invokes MailService.sendMessage() using the e-mail address it retrieved from the message. We can use MockEJB to deploy a spy MailService object with the appropriate JNDI name, and then verify that the “To address” parameter passed in to the MailService is the same as the one retrieved from the incoming message. Because MailService is an interface, we applied the Self-Shunt Pattern (see recipe 11.2) and had the test case class implement MailService itself. The Spy implementation of this method verifies two things:

that the message-driven bean invoked the MailService at all, and that it invoked sendMessage() with the appropriate “To address.”

As we wrote previously, using MockEJB to deploy the message-driven bean is the easy part. The difficult part—and it only gets worse as your message-driven beans become more complex—is dealing with the fact that the object under test

426 CHAPTER 11

Testing Enterprise JavaBeans

manages its own collaborators. You need to attack this testing problem on a case- by-case basis using perhaps any technique from the rest of this book.

Discussion

MockEJB makes it simple to simulate deploying and to invoke your message- driven bean, and for simple beans that is enough; but, most message-driven beans need to collaborate with other objects, which complicates matters. To cope with that complexity, we recommend moving the remaining message-consuming and message-processing logic out to separate classes, as we describe in recipe 11.8. The alternative is to test the message-driven bean in a live container, with all the addi- tional issues that it raises (see recipe 11.7).

One thing you will notice about our MockEJB test is that it does not actually depend on the message-driven bean class ProcessOrderSubmissionLegacyBean. Instead, it treats the message-driven bean as just a MessageListener. As the test only ever invokes onMessage(), part of the generic MessageListener interface, there is no need to cast the object to the specific message-driven bean class.

Unusual perhaps, but true. Anything to make the tests simpler.

Related

■ 2.11—Test an object that instantiates other objects

■ 11.7—Test a message-driven bean inside the container

■ 11.8—Test a message-driven bean outside the container

Một phần của tài liệu Manning JUnit recipes practical methods for program (Trang 453 - 457)

Tải bản đầy đủ (PDF)

(753 trang)