
- •contents
- •preface
- •acknowledgments
- •about this book
- •Special features
- •Best practices
- •Design patterns in action
- •Software directory
- •Roadmap
- •Part 1: JUnit distilled
- •Part 2: Testing strategies
- •Part 3: Testing components
- •Code
- •References
- •Author online
- •about the authors
- •about the title
- •about the cover illustration
- •JUnit jumpstart
- •1.1 Proving it works
- •1.2 Starting from scratch
- •1.3 Understanding unit testing frameworks
- •1.4 Setting up JUnit
- •1.5 Testing with JUnit
- •1.6 Summary
- •2.1 Exploring core JUnit
- •2.2 Launching tests with test runners
- •2.2.1 Selecting a test runner
- •2.2.2 Defining your own test runner
- •2.3 Composing tests with TestSuite
- •2.3.1 Running the automatic suite
- •2.3.2 Rolling your own test suite
- •2.4 Collecting parameters with TestResult
- •2.5 Observing results with TestListener
- •2.6 Working with TestCase
- •2.6.1 Managing resources with a fixture
- •2.6.2 Creating unit test methods
- •2.7 Stepping through TestCalculator
- •2.7.1 Creating a TestSuite
- •2.7.2 Creating a TestResult
- •2.7.3 Executing the test methods
- •2.7.4 Reviewing the full JUnit life cycle
- •2.8 Summary
- •3.1 Introducing the controller component
- •3.1.1 Designing the interfaces
- •3.1.2 Implementing the base classes
- •3.2 Let’s test it!
- •3.2.1 Testing the DefaultController
- •3.2.2 Adding a handler
- •3.2.3 Processing a request
- •3.2.4 Improving testProcessRequest
- •3.3 Testing exception-handling
- •3.3.1 Simulating exceptional conditions
- •3.3.2 Testing for exceptions
- •3.4 Setting up a project for testing
- •3.5 Summary
- •4.1 The need for unit tests
- •4.1.1 Allowing greater test coverage
- •4.1.2 Enabling teamwork
- •4.1.3 Preventing regression and limiting debugging
- •4.1.4 Enabling refactoring
- •4.1.5 Improving implementation design
- •4.1.6 Serving as developer documentation
- •4.1.7 Having fun
- •4.2 Different kinds of tests
- •4.2.1 The four flavors of software tests
- •4.2.2 The three flavors of unit tests
- •4.3 Determining how good tests are
- •4.3.1 Measuring test coverage
- •4.3.2 Generating test coverage reports
- •4.3.3 Testing interactions
- •4.4 Test-Driven Development
- •4.4.1 Tweaking the cycle
- •4.5 Testing in the development cycle
- •4.6 Summary
- •5.1 A day in the life
- •5.2 Running tests from Ant
- •5.2.1 Ant, indispensable Ant
- •5.2.2 Ant targets, projects, properties, and tasks
- •5.2.3 The javac task
- •5.2.4 The JUnit task
- •5.2.5 Putting Ant to the task
- •5.2.6 Pretty printing with JUnitReport
- •5.2.7 Automatically finding the tests to run
- •5.3 Running tests from Maven
- •5.3.2 Configuring Maven for a project
- •5.3.3 Executing JUnit tests with Maven
- •5.3.4 Handling dependent jars with Maven
- •5.4 Running tests from Eclipse
- •5.4.1 Creating an Eclipse project
- •5.4.2 Running JUnit tests in Eclipse
- •5.5 Summary
- •6.1 Introducing stubs
- •6.2 Practicing on an HTTP connection sample
- •6.2.1 Choosing a stubbing solution
- •6.2.2 Using Jetty as an embedded server
- •6.3 Stubbing the web server’s resources
- •6.3.1 Setting up the first stub test
- •6.3.2 Testing for failure conditions
- •6.3.3 Reviewing the first stub test
- •6.4 Stubbing the connection
- •6.4.1 Producing a custom URL protocol handler
- •6.4.2 Creating a JDK HttpURLConnection stub
- •6.4.3 Running the test
- •6.5 Summary
- •7.1 Introducing mock objects
- •7.2 Mock tasting: a simple example
- •7.3 Using mock objects as a refactoring technique
- •7.3.1 Easy refactoring
- •7.3.2 Allowing more flexible code
- •7.4 Practicing on an HTTP connection sample
- •7.4.1 Defining the mock object
- •7.4.2 Testing a sample method
- •7.4.3 Try #1: easy method refactoring technique
- •7.4.4 Try #2: refactoring by using a class factory
- •7.5 Using mocks as Trojan horses
- •7.6 Deciding when to use mock objects
- •7.7 Summary
- •8.1 The problem with unit-testing components
- •8.2 Testing components using mock objects
- •8.2.1 Testing the servlet sample using EasyMock
- •8.2.2 Pros and cons of using mock objects to test components
- •8.3 What are integration unit tests?
- •8.4 Introducing Cactus
- •8.5 Testing components using Cactus
- •8.5.1 Running Cactus tests
- •8.5.2 Executing the tests using Cactus/Jetty integration
- •8.6 How Cactus works
- •8.6.2 Stepping through a test
- •8.7 Summary
- •9.1 Presenting the Administration application
- •9.2 Writing servlet tests with Cactus
- •9.2.1 Designing the first test
- •9.2.2 Using Maven to run Cactus tests
- •9.2.3 Finishing the Cactus servlet tests
- •9.3 Testing servlets with mock objects
- •9.3.1 Writing a test using DynaMocks and DynaBeans
- •9.3.2 Finishing the DynaMock tests
- •9.4 Writing filter tests with Cactus
- •9.4.1 Testing the filter with a SELECT query
- •9.4.2 Testing the filter for other query types
- •9.4.3 Running the Cactus filter tests with Maven
- •9.5 When to use Cactus, and when to use mock objects
- •9.6 Summary
- •10.1 Revisiting the Administration application
- •10.2 What is JSP unit testing?
- •10.3 Unit-testing a JSP in isolation with Cactus
- •10.3.1 Executing a JSP with SQL results data
- •10.3.2 Writing the Cactus test
- •10.3.3 Executing Cactus JSP tests with Maven
- •10.4 Unit-testing taglibs with Cactus
- •10.4.1 Defining a custom tag
- •10.4.2 Testing the custom tag
- •10.5 Unit-testing taglibs with mock objects
- •10.5.1 Introducing MockMaker and installing its Eclipse plugin
- •10.5.2 Using MockMaker to generate mocks from classes
- •10.6 When to use mock objects and when to use Cactus
- •10.7 Summary
- •Unit-testing database applications
- •11.1 Introduction to unit-testing databases
- •11.2 Testing business logic in isolation from the database
- •11.2.1 Implementing a database access layer interface
- •11.2.2 Setting up a mock database interface layer
- •11.2.3 Mocking the database interface layer
- •11.3 Testing persistence code in isolation from the database
- •11.3.1 Testing the execute method
- •11.3.2 Using expectations to verify state
- •11.4 Writing database integration unit tests
- •11.4.1 Filling the requirements for database integration tests
- •11.4.2 Presetting database data
- •11.5 Running the Cactus test using Ant
- •11.5.1 Reviewing the project structure
- •11.5.2 Introducing the Cactus/Ant integration module
- •11.5.3 Creating the Ant build file step by step
- •11.5.4 Executing the Cactus tests
- •11.6 Tuning for build performance
- •11.6.2 Grouping tests in functional test suites
- •11.7.1 Choosing an approach
- •11.7.2 Applying continuous integration
- •11.8 Summary
- •Unit-testing EJBs
- •12.1 Defining a sample EJB application
- •12.2 Using a façade strategy
- •12.3 Unit-testing JNDI code using mock objects
- •12.4 Unit-testing session beans
- •12.4.1 Using the factory method strategy
- •12.4.2 Using the factory class strategy
- •12.4.3 Using the mock JNDI implementation strategy
- •12.5 Using mock objects to test message-driven beans
- •12.6 Using mock objects to test entity beans
- •12.7 Choosing the right mock-objects strategy
- •12.8 Using integration unit tests
- •12.9 Using JUnit and remote calls
- •12.9.1 Requirements for using JUnit directly
- •12.9.2 Packaging the Petstore application in an ear file
- •12.9.3 Performing automatic deployment and execution of tests
- •12.9.4 Writing a remote JUnit test for PetstoreEJB
- •12.9.5 Fixing JNDI names
- •12.9.6 Running the tests
- •12.10 Using Cactus
- •12.10.1 Writing an EJB unit test with Cactus
- •12.10.2 Project directory structure
- •12.10.3 Packaging the Cactus tests
- •12.10.4 Executing the Cactus tests
- •12.11 Summary
- •A.1 Getting the source code
- •A.2 Source code overview
- •A.3 External libraries
- •A.4 Jar versions
- •A.5 Directory structure conventions
- •B.1 Installing Eclipse
- •B.2 Setting up Eclipse projects from the sources
- •B.3 Running JUnit tests from Eclipse
- •B.4 Running Ant scripts from Eclipse
- •B.5 Running Cactus tests from Eclipse
- •references
- •index

Using mock objects to test message-driven beans |
307 |
|
|
The side effect is that the first test you run results in the OrderLocalHome mock being cached, along with the expectations set on it. Thus, the behavior of the create method defined in listing 12.11 (mockOrderLocalHome.expectAndThrow("cre-
ate", C.ANY_ARGS, new CreateException("error"))) isn’t used; instead, the behavior from the previous test is used (mockOrderLocalHome.matchAndReturn("create", C.ANY_ARGS, orderLocal)), leading to an error.
To fix this problem, you must reset the objects in their pristine states before each test. This is normally assured by the JUnit framework—except, of course, when you use static variables. One solution is to introduce an OrderUtil.clearCache method that you call in the TestOrderUtil.setUp method:
public class OrderUtil
{
private static OrderLocalHome orderLocalHome; protected static void clearCache()
{
orderLocalHome = null;
}
protected static OrderLocalHome getOrderHome()
{
[...]
}
[...]
public class TestOrderUtil extends CommonPetstoreTestCase
{
[...]
protected void setUp() throws Exception
{
super.setUp();
OrderUtil.clearCache();
}
[...]
Running the tests now succeeds, as shown in figure 12.3.
12.5 Using mock objects to test message-driven beans
Unit-testing MDBs is easy when you use a mock-objects approach. The reason is that the JMS API is well designed: It uses a lot of interfaces and, in most cases, uses an IOC strategy by passing all the needed objects to method calls. Thus all the object instantiations are done in the client code, which makes it easier to control and mock. Let’s see what this means on the simple Petstore application

308CHAPTER 12
Unit-testing EJBs
Figure 12.3 Successful test after fixing the EJB home caching issue that prevented you from running the unit tests independently of one another
by unit-testing the OrderProcessorMDB MDB (see figure 12.1). The method to unit test, onMessage, is shown in listing 12.12.
Listing 12.12 OrderProcessorMDB.java
package junitbook.ejb.service;
import javax.ejb.EJBException; import javax.ejb.MessageDrivenBean;
import javax.ejb.MessageDrivenContext; import javax.jms.Message;
import javax.jms.MessageListener; import javax.jms.ObjectMessage;
import junitbook.ejb.domain.OrderLocal; import junitbook.ejb.domain.OrderUtil;
public class OrderProcessorMDB
implements MessageDrivenBean, MessageListener
{
public void onMessage(Message recvMsg)
{
ObjectMessage msg = (ObjectMessage) recvMsg;
Integer orderId;
try
{
orderId = (Integer) msg.getObject(); OrderLocal order = OrderUtil.getOrder(orderId); proceedOrder(order);
}
catch (Exception e)
{
throw new EJBException("Error processing order...");
}
}
private void proceedOrder(OrderLocal order) throws Exception

Using mock objects to test message-driven beans |
309 |
|
|
{
//Perform some business logic here and notify the customer
//possibly by sending an email.
}
public void ejbCreate() {}
public void setMessageDrivenContext( MessageDrivenContext context) {}
public void ejbRemove() {}
}
This is similar to what you did in the previous sections when unit-testing session beans. Like session beans, MDBs can be unit-tested using several mock objects techniques: the factory method approach, the factory class strategy, or the mock JNDI implementation approach.
Let’s use the mock JNDI implementation strategy and reuse the CommonPetstoreTestCase test case from listing 12.10. The resulting test case is shown in listing 12.13.
Listing 12.13 Unit test for OrderProcessorMDB.onMessage
package junitbook.ejb.service;
import javax.jms.ObjectMessage;
import com.mockobjects.dynamic.Mock;
import junit.framework.Test;
import junitbook.ejb.CommonPetstoreTestCase; import junitbook.ejb.service.OrderProcessorMDB;
public class TestOrderProcessorMDB extends CommonPetstoreTestCase
{
private OrderProcessorMDB orderProcessor; |
|
|
public static Test suite() |
|
b Required to |
|
||
{ |
|
initialize |
return suite(TestOrderProcessorMDB.class); |
|
CommonPetstore- |
|
||
} |
|
TestCase |
protected void setUp() throws Exception |
|
|
{ |
|
|
super.setUp(); |
|
|
orderProcessor = new OrderProcessorMDB(); |
|
|
} |
|
|
public void testOnMessageOk() throws Exception |
|
|
{ |
|
|
Mock mockMessage = new Mock(ObjectMessage.class);

310CHAPTER 12
Unit-testing EJBs
ObjectMessage message =
(ObjectMessage) mockMessage.proxy();
mockMessage.expectAndReturn("getObject", new Integer(1234));
orderProcessor.onMessage(message);
mockMessage.verify();
}
}
12.6 Using mock objects to test entity beans
Entity beans are the easiest to unit-test in isolation, especially if they are CMP entity beans. In that case, they are very much like standard Java beans. (For BMP entity beans, refer to chapter 11, “Unit-testing database applications,” which shows how to unit-test JDBC code.)
Listing 12.14 shows the code for the OrderEJB CMP EB from the Petstore application (see figure 12.1).
Listing 12.14 OrderEJB.java
package junitbook.ejb.domain;
import java.rmi.RemoteException; import java.util.Date;
import javax.ejb.CreateException; import javax.ejb.EJBException; import javax.ejb.EntityBean; import javax.ejb.EntityContext; import javax.ejb.RemoveException;
public abstract class OrderEJB implements EntityBean
{
public abstract Integer getOrderId();
public abstract void setOrderId(Integer orderId); public abstract Date getOrderDate();
public abstract void setOrderDate(Date orderDate); public abstract String getOrderItem();
public abstract void setOrderItem(String item);
public OrderLocal ejbCreate(Date orderDate, String orderItem) throws CreateException
{
int uid = 0;
// Note: Would need a real counter here. This is a hack! uid = orderDate.hashCode() + orderItem.hashCode();
setOrderId(new Integer(uid));

Using mock objects to test entity beans |
311 |
|
|
setOrderDate(orderDate);
setOrderItem(orderItem);
return null;
}
public void ejbPostCreate(Date orderDate, String orderItem) throws CreateException {}
public void ejbActivate()
throws EJBException, RemoteException {} public void ejbLoad()
throws EJBException, RemoteException {} public void ejbPassivate()
throws EJBException, RemoteException {} public void ejbRemove()
throws RemoveException, EJBException, RemoteException {} public void ejbStore()
throws EJBException, RemoteException {}
public void setEntityContext(EntityContext context) throws EJBException, RemoteException {}
public void unsetEntityContext()
throws EJBException, RemoteException {}
}
Let’s unit-test the ejbCreate method. The only issue is that when you’re using the EJB 2.0 specification, a CMP EB is an abstract class and the field getters and setters are not implemented. You need to create a class that extends OrderEJB and implements the getters and setters. (Note that doing so is very easy with a good IDE, because every good IDE has a “Generate Getters and Setters” feature.) Let’s implement it as an inner class of the TestCase (see listing 12.15).
Listing 12.15 Unit-testing OrderEJB.ejbCreate
package junitbook.ejb.domain;
import java.util.Date;
import junit.framework.TestCase;
public class TestOrderEJB extends TestCase
{
public class TestableOrderEJB extends OrderEJB
{
private Integer orderId; private String item; private Date date;
public Integer getOrderId() { return this.orderId; }
Make abstract EJB testable