◆ Solution
Starting with a BMP entity bean that implements everything in one place, we applied a few refactorings to reach a final design that is significantly easier to test.
Here is where we started—this is the original BMP entity bean. This listing is par- ticularly long at about 250 lines, and it is just the beginning. There is a large amount of code in this solution.
package junit.cookbook.coffee.model.ejb;
import java.rmi.RemoteException;
import java.sql.*;
import javax.ejb.*;
import javax.naming.*;
import javax.sql.DataSource;
import junit.cookbook.coffee.data.*;
public class OrderBmpBean implements EntityBean { private EntityContext context;
private DataSource dataSource;
private Integer customerId;
public Integer getCustomerId() { return customerId;
}
public Integer ejbFindByPrimaryKey(Integer orderId) throws FinderException, RemoteException { Connection connection = null;
PreparedStatement statement = null;
try {
connection = getConnection();
statement =
connection.prepareStatement(
"select orderId from orders.orders "
+ "where orderId = ?");
statement.setInt(1, orderId.intValue());
ResultSet resultSet = statement.executeQuery();
if (resultSet.next() == false) { throw new ObjectNotFoundException(
"Order ID <" + orderId.toString() + ">");
}
Listing A.12 A BMP entity bean for orders
654 APPENDIX A Complete solutions
return orderId;
}
catch (NamingException report) {
throw new FinderException(report.toString());
}
catch (SQLException report) {
throw new FinderException(report.toString());
}
finally { try {
if (statement != null) statement.close();
if (connection != null) connection.close();
}
catch (SQLException ignored) { }
} }
public Integer ejbCreate(Integer orderId, Integer customerId) throws CreateException, RemoteException {
this.customerId = customerId;
Connection connection = null;
PreparedStatement statement = null;
try {
connection = getConnection();
statement =
connection.prepareStatement(
"insert into orders.orders (orderId, customerId) "
+ "values (?, ?)");
statement.setInt(1, orderId.intValue());
statement.setInt(2, customerId.intValue());
statement.executeUpdate();
return orderId;
}
catch (Exception wrap) { throw new EJBException(
"Unable to create order with ID <" + orderId + ">", wrap);
}
finally { try {
if (statement != null) statement.close();
if (connection != null)
655 Test a BMP entity bean
connection.close();
}
catch (SQLException ignored) { }
} }
public void ejbPostCreate(Integer orderId, Integer customerId) { }
private Connection getConnection()
throws SQLException, NamingException { return getDataSource().getConnection();
}
private DataSource getDataSource() throws NamingException { if (dataSource == null) {
Context rootContext = new InitialContext();
Object object =
rootContext.lookup("java:/comp/env/jdbc/OrderData");
dataSource = (DataSource) object;
}
return dataSource;
}
public void ejbLoad() throws EJBException, RemoteException { Connection connection = null;
PreparedStatement statement = null;
ResultSet resultSet = null;
Integer orderId = (Integer) context.getPrimaryKey();
try {
connection = getConnection();
statement =
connection.prepareStatement(
"select * from orders.orders where orderId = ?");
statement.setInt(1, orderId.intValue());
resultSet = statement.executeQuery();
resultSet.next();
customerId = new Integer(resultSet.getInt("customerId"));
}
catch (Exception wrap) { throw new EJBException(
"Unable to load order with ID <" + orderId + ">", wrap);
}
finally { try {
if (resultSet != null) resultSet.close();
656 APPENDIX A Complete solutions
if (statement != null) statement.close();
if (connection != null) connection.close();
}
catch (SQLException ignored) { }
} }
public void ejbRemove()
throws RemoveException, EJBException, RemoteException { Connection connection = null;
PreparedStatement statement = null;
Integer orderId = (Integer) context.getPrimaryKey();
try {
connection = getConnection();
statement =
connection.prepareStatement(
"delete from orders.orders where orderId = ?");
statement.setInt(1, orderId.intValue());
statement.executeUpdate();
}
catch (Exception wrap) { throw new EJBException(
"Unable to remove order with ID <" + orderId + ">", wrap);
}
finally { try {
if (statement != null) statement.close();
if (connection != null) connection.close();
}
catch (SQLException ignored) { }
} }
public void ejbStore() throws EJBException, RemoteException { Connection connection = null;
PreparedStatement statement = null;
Integer orderId = (Integer) context.getPrimaryKey();
try {
connection = getConnection();
657 Test a BMP entity bean
statement =
connection.prepareStatement(
"update orders.orders set customerId = ? "
+ "where orderId = ?");
statement.setInt(1, customerId.intValue());
statement.setInt(2, orderId.intValue());
statement.executeUpdate();
}
catch (Exception wrap) { throw new EJBException(
"Unable to store order with ID <" + orderId + ">", wrap);
}
finally { try {
if (statement != null) statement.close();
if (connection != null) connection.close();
}
catch (SQLException ignored) { }
} }
public void setEntityContext(EntityContext context) throws EJBException, RemoteException {
this.context = context;
}
public void unsetEntityContext()
throws EJBException, RemoteException { this.context = null;
}
public void ejbActivate() throws EJBException, RemoteException { }
public void ejbPassivate() throws EJBException, RemoteException { }
}
In its current state, this entity bean needs to be tested in the container, and so it requires working test data, a correctly populated JNDI directory, and all the associ- ated startup and shutdown costs. First, we moved almost all the code out to a new class, leaving behind only JNDI lookups and managing the primary key. Here is the entity bean after this refactoring.
658 APPENDIX A Complete solutions
package junit.cookbook.coffee.model.ejb;
import java.rmi.RemoteException;
import javax.ejb.*;
import javax.naming.*;
import javax.sql.DataSource;
import junit.cookbook.coffee.data.OrderRow;
import junit.cookbook.coffee.data.jdbc.*;
public class OrderBmpBean implements EntityBean { private EntityContext context;
private OrderBmpBeanLogic logic;
public String getCustomerId() { return logic.getCustomerId();
}
public void setCustomerId(String customerId) { logic.setCustomerId(customerId);
}
public void setEntityContext(EntityContext context) throws EJBException, RemoteException {
this.context = context;
makeBeanLogicObject();
}
protected void makeBeanLogicObject() {
DataSourceConnectionProvider connectionProvider =
new DataSourceConnectionProvider(lookupDataSource());
OrderStoreCommandExecuter orderStoreCommandExecuter = new OrderStoreCommandExecuterJdbcImpl(
new OrderStoreCommandExecuterEjbImpl(
new SimpleOrderStoreCommandExecuter()), connectionProvider);
this.logic =
new OrderBmpBeanLogic(
new OrderStoreJdbcImpl(), orderStoreCommandExecuter);
}
public void unsetEntityContext()
throws EJBException, RemoteException { this.context = null;
}
public Integer ejbFindByPrimaryKey(Integer orderId) throws FinderException, RemoteException { Listing A.13 A thinner BMP entity bean for orders
659 Test a BMP entity bean
return logic.ejbFindByPrimaryKey(orderId);
}
public Integer ejbCreate(Integer orderId, String customerId) throws CreateException, RemoteException {
return logic.ejbCreate(orderId, customerId);
}
public void ejbPostCreate(Integer orderId, String customerId) { }
public void ejbLoad() throws EJBException, RemoteException { logic.ejbLoad(getOrderId());
}
public void ejbRemove()
throws RemoveException, EJBException, RemoteException { logic.ejbRemove(getOrderId());
}
public void ejbStore() throws EJBException, RemoteException { logic.ejbStore();
}
public void ejbActivate() throws EJBException, RemoteException { }
public void ejbPassivate() throws EJBException, RemoteException { }
public static DataSource lookupDataSource() { try {
Context rootContext = new InitialContext();
Object object =
rootContext.lookup("java:comp/env/jdbc/OrderData");
return (DataSource) object;
}
catch (NamingException wrap) { throw new EJBException(
"Unable to retrieve data source", wrap);
} }
public Integer getOrderId() {
return (Integer) context.getPrimaryKey();
} }
There is a combination of paper-thin EJB lifecycle methods, a JNDI lookup, and a considerable amount of work to create the OrderBmpBeanLogic object, which we
660 APPENDIX A Complete solutions
have highlighted in bold print. This is where most of the action now takes place.
This Bean Logic class uses an OrderStore—a persistent store for order objects—
and a specialized OrderStoreCommandExecuter, which hides the ugliness of han- dling JDBC exceptions and generating EJB exceptions. The OrderStore is modeled after the CatalogStore we developed in chapter 10, “Testing and JDBC,” so we sim- ply present the interface first, and then the JDBC implementation that our entity bean needs to store order objects in the database. The OrderStore operates on OrderRow objects—each represents a row in the orders.orders table, relating each order to the customer who placed it, using their primary keys.
package junit.cookbook.coffee.data;
public interface OrderStore { boolean exists(Integer orderId);
void create(OrderRow orderRow);
OrderRow findByOrderId(Integer orderId);
void remove(Integer orderId);
void update(final OrderRow orderRow);
}
Each of these methods corresponds to an EJB lifecycle method. For example, OrderBmpBean.ejbFindByPrimaryKey() needs to check that the OrderRow exists using OrderStore.exists(). The JDBC implementation of OrderStore uses the Diasparsoft Toolkit JDBC classes to simplify its implementation. The result is the following class named OrderStoreJdbcImpl.
package junit.cookbook.coffee.data.jdbc;
import java.sql.*;
import java.util.*;
import junit.cookbook.coffee.data.*;
import junit.cookbook.coffee.model.*;
import com.diasparsoftware.java.sql.PreparedStatementData;
import com.diasparsoftware.jdbc.*;
public class OrderStoreJdbcImpl implements OrderStore { private JdbcQueryExecuter executer;
public void open(Connection connection) {
this.setExecuter(new JdbcQueryExecuter(connection));
}
public void close() { this.executer = null;
}
Listing A.14 JDBC implementation of OrderStore
TE AM FL Y
Team-Fly®
661 Test a BMP entity bean
private JdbcQueryExecuter getExecuter() { if (executer == null) {
throw new IllegalStateException(
"Please provide me with a database connection first.");
}
return executer;
}
private void setExecuter(JdbcQueryExecuter executer) { this.executer = executer;
}
public boolean exists(Integer orderId) {
PreparedStatementData countOrdersByIdStatementData = new PreparedStatementData(
"select count(orderId) from orders.orders "
+ "where orderId = ?",
Collections.singletonList(orderId));
int rowCount =
getExecuter().executeCountStatement(
countOrdersByIdStatementData);
return (rowCount > 0);
}
public void create(final OrderRow orderRow) { List parameters = new ArrayList() { {
add(orderRow.orderId);
add(orderRow.customerId);
} };
PreparedStatementData insertStatementData = new PreparedStatementData(
"insert into orders.orders (orderId, customerId) "
+ "values (?, ?)", parameters);
getExecuter().executeInsertStatement(insertStatementData);
}
public OrderRow findByOrderId(final Integer orderId) { PreparedStatementData selectStatementData = new PreparedStatementData(
"select * from orders.orders where orderId = ?", Collections.singletonList(orderId));
List orders =
getExecuter().executeSelectStatement(
selectStatementData, new OrderRowMapper());
662 APPENDIX A Complete solutions
return (OrderRow) orders.get(0);
}
public void remove(Integer orderId) {
PreparedStatementData deleteStatementData = new PreparedStatementData(
"delete from orders.orders where orderId = ?", Collections.singletonList(orderId));
getExecuter().executeDeleteStatement(deleteStatementData);
}
public void update(final OrderRow orderRow) { List parameters = new ArrayList() { {
add(orderRow.customerId);
add(orderRow.orderId);
} };
PreparedStatementData updateStatementData = new PreparedStatementData(
"update orders.orders set customerId = ? "
+ "where orderId = ?", parameters);
getExecuter().executeUpdateStatement(updateStatementData);
}
public static class OrderRowMapper extends JdbcRowMapper { public Object makeDomainObject(ResultSet row)
throws SQLException {
Integer orderId = (Integer) row.getObject("orderId");
String customerId = row.getString("customerId");
return new OrderRow(orderId, customerId);
} } }
Each of the methods of this class follows the same basic rhythm: collect the parame- ters, create a PreparedStatementData object, and then execute the corresponding SQL statement. To enable transactional behavior, we want the JDBC connection to be passed in from outside. For this reason, we originally had a constructor that took a connection as a parameter; however, to avoid unnecessary object creation and to simplify other code, we moved to an open/close design: to use the JDBC- based OrderStore, you “open” it with a database connection, then “close” it when you have finished. Closing the JDBC-based OrderStore does not close the database connection—never close connections you do not own—but causes it to release its
663 Test a BMP entity bean
reference to the connection, so as not to leak connections. So much for Order- Store and its JDBC-based implementation.
Next are the OrderStoreCommandExecuters. These evolved when we extracted duplication out of the original entity bean’s lifecycle methods. Notably, each method was doing two things:
■ Obtaining a database connection, invoking some JDBC client code, and closing the database connection.
■ Invoking some OrderStore code, and wrapping all DataStoreExceptions into EJBExceptions.
We wanted to remove this duplication, so we extracted the first pattern into its own method, then moved that method into its own class. Here is the result. When- ever we see “invoke some code” inside a recurring implementation pattern, we know that the Command pattern is lurking in there, and sure enough, as we refac- tored, the class OrderStoreCommand appeared, as if out of nowhere. We had origi- nally implemented the JDBC client code in Closure objects,4 but realized that a Command is essentially just a type-safe Closure, so we created the class Order- StoreCommand and the associated command executers. This first one ensures that when we execute an OrderStoreCommand on a JDBC-based OrderStore, we manage the database connection correctly.
package junit.cookbook.coffee.data.jdbc;
import java.sql.*;
import junit.cookbook.coffee.data.*;
public class OrderStoreCommandExecuterJdbcImpl implements OrderStoreCommandExecuter {
private OrderStoreCommandExecuter executer;
private ConnectionProvider connectionProvider;
public OrderStoreCommandExecuterJdbcImpl(
OrderStoreCommandExecuter executer, ConnectionProvider connectionProvider) { this.executer = executer;
this.connectionProvider = connectionProvider;
}
4 See the Jakarta Commons Collections package for more about the interface Closure. (http://jakarta.apache.org/commons/collections.html)
Listing A.15 OrderStoreCommand
664 APPENDIX A Complete solutions
public void execute(
OrderStore orderStore,
OrderStoreCommand orderStoreCommand, String exceptionMessage) {
OrderStoreJdbcImpl orderStoreJdbcImpl = (OrderStoreJdbcImpl) orderStore;
Connection connection = connectionProvider.getConnection();
orderStoreJdbcImpl.open(connection);
try {
executer.execute(
orderStoreJdbcImpl, orderStoreCommand, exceptionMessage);
}
finally { try {
orderStoreJdbcImpl.close();
connection.close();
}
catch (SQLException ignored) { }
} } }
You can see how this class implements the pattern: obtain a connection, open the JDBC-based OrderStore, execute the JDBC client code (the “order store com- mand”), and then ensure (with a finally block) that the connection is closed. We also implemented the second pattern—wrapping a DataStoreException inside an EJBException—as a command executer. Here is the code.
package junit.cookbook.coffee.model.ejb;
import javax.ejb.EJBException;
import junit.cookbook.coffee.data.*;
import com.diasparsoftware.store.DataStoreException;
public class OrderStoreCommandExecuterEjbImpl implements OrderStoreCommandExecuter { private OrderStoreCommandExecuter executer;
public OrderStoreCommandExecuterEjbImpl(
OrderStoreCommandExecuter executer) {
this.executer = executer;
}
Listing A.16 EJB-based OrderStoreCommand executer
665 Test a BMP entity bean
public void execute(
OrderStore orderStore,
OrderStoreCommand orderStoreCommand, String exceptionMessage) {
try {
executer.execute(
orderStore, orderStoreCommand, exceptionMessage);
}
catch (DataStoreException wrap) {
throw new EJBException(exceptionMessage, wrap);
} } }
These command executers implement the Decorator pattern: each performs its specific work, then asks the next command executer to do its work, eventually ending with a command executer that simply executes the command. This pat- tern makes it simpler to add behavior to the act of executing a command by liter- ally adding behavior on top of behavior. We can reuse the JDBC Decorator without the EJB Decorator when we abandon BMP entity beans and move to plain- vanilla Java objects using JDBC. All we have to do is create a JDBC-based command executer that wraps the “just execute the command” command executer, which we named SimpleOrderStoreCommandExecuter. Here is the code:
package junit.cookbook.coffee.data;
public class SimpleOrderStoreCommandExecuter implements OrderStoreCommandExecuter { public void execute(
OrderStore orderStore,
OrderStoreCommand orderStoreCommand, String exceptionMessage) {
orderStoreCommand.execute(orderStore);
} }
We think this is worthy of a name starting with “simple.” It even ignores the excep- tion message, as it does not handle any exceptions. Returning to OrderBmpBean- Logic, we see how these command executers fit together.
OrderStoreCommandExecuter orderStoreCommandExecuter = new OrderStoreCommandExecuterJdbcImpl(
new OrderStoreCommandExecuterEjbImpl(
666 APPENDIX A Complete solutions
new SimpleOrderStoreCommandExecuter()), connectionProvider);
When OrderBmpBeanLogic uses this command executer to execute an OrderStore- Command, it first manages the database connection properly (JDBC command exe- cuter), then wraps any exceptions in an EJBException (EJB command executer), then finally executes the command. If we were to reuse the OrderBmpBeanLogic out- side the context of an EJB (and really at that point we should invent a better name), then we could change the above code to the following, removing the EJB command executer and thereby letting DataStoreExceptions be reported as they are. If you were to remove EJBs from a typically designed J2EE application, think of all the exception handlers you would have to change! Here, it is a one-line change.
OrderStoreCommandExecuter orderStoreCommandExecuter = new OrderStoreCommandExecuterJdbcImpl(
new SimpleOrderStoreCommandExecuter()), connectionProvider);
No more EJBExceptions! It really is that simple. Now enough is enough: let us look at OrderBeanBmpLogic. It contains methods that correspond to the entity bean lifecycle methods, and to create one of these, you supply an OrderStore and a command executer. This class essentially does what the corresponding entity bean would do without relying on an EJB container or even a database-aware OrderStore. It would work entirely in memory too!
package junit.cookbook.coffee.model.ejb;
import java.rmi.RemoteException;
import javax.ejb.*;
import junit.cookbook.coffee.data.*;
public class OrderBmpBeanLogic { private OrderStore orderStore;
private OrderStoreCommandExecuter executer;
private OrderRow orderRow;
public OrderBmpBeanLogic(
OrderStore orderStore,
OrderStoreCommandExecuter executer) { this.orderStore = orderStore;
this.executer = executer;
}
public Integer ejbFindByPrimaryKey(final Integer orderId) throws ObjectNotFoundException {
Listing A.17 Business logic POJO for orders
667 Test a BMP entity bean
OrderStoreCommand orderStoreLogic = new OrderStoreCommand() { private boolean orderExists;
public void execute(OrderStore orderStore) { orderExists = orderStore.exists(orderId);
}
public Object getReturnValue() { return new Boolean(orderExists);
} };
String failureMessage =
"Unable to find order with ID <" + orderId + ">";
executer.execute(orderStore, orderStoreLogic, failureMessage);
boolean orderExists =
((Boolean) orderStoreLogic.getReturnValue()) .booleanValue();
if (orderExists) return orderId;
else
throw new ObjectNotFoundException(failureMessage);
}
public Integer ejbCreate(Integer orderId, String customerId) { orderRow = new OrderRow(orderId, customerId);
OrderStoreCommand command = new OrderStoreCommand() { public void execute(OrderStore orderStore) { orderStore.create(orderRow);
} };
String failureMessage =
"Unable to create order with ID <"
+ orderRow.orderId + ">";
executer.execute(orderStore, command, failureMessage);
return orderId;
}
public void ejbLoad(final Integer orderId) {
OrderStoreCommand command = new OrderStoreCommand() { private OrderRow orderRow;
public void execute(OrderStore orderStore) { orderRow = orderStore.findByOrderId(orderId);
}
public Object getReturnValue() { return orderRow;
668 APPENDIX A Complete solutions
} };
String failureMessage =
"Unable to load order with ID <" + orderId + ">";
executer.execute(orderStore, command, failureMessage);
orderRow = (OrderRow) command.getReturnValue();
}
public void ejbStore() {
OrderStoreCommand command = new OrderStoreCommand() { public void execute(OrderStore orderStore) { orderStore.update(orderRow);
} };
String failureMessage =
"Unable to store order with ID <"
+ orderRow.orderId + ">";
executer.execute(orderStore, command, failureMessage);
}
public void ejbRemove(final Integer orderId) {
OrderStoreCommand command = new OrderStoreCommand() { public void execute(OrderStore orderStore) { orderStore.remove(orderId);
} };
String failureMessage =
"Unable to remove order with ID <" + orderId + ">";
executer.execute(orderStore, command, failureMessage);
}
public String getCustomerId() { return orderRow.customerId;
}
public void setCustomerId(String customerId) { orderRow.customerId = customerId;
}
public OrderRow getOrderRow() { return orderRow;
} }
Each method has the same rhythm: create an OrderStoreCommand, create a failure message in case any exceptions are thrown, and then execute the command.
669 Test a BMP entity bean
Some commands have return values for those methods that return a value. Look at how simple the commands are: they merely delegate their work to the Order- Store, which defines a method for each command. The nice thing about this class is that it works with any kind of OrderStore (not just ones that use a database) and provides maximum flexibility for executing the OrderStoreCommands. In particu- lar, testing OrderBeanBmpLogic is a breeze. Here are a few tests, using EasyMock to fake out the OrderStore and the command executer.
package junit.cookbook.coffee.model.ejb.test;
import java.sql.SQLException;
import javax.ejb.*;
import junit.cookbook.coffee.data.*;
import junit.cookbook.coffee.model.ejb.OrderBmpBeanLogic;
import junit.framework.TestCase;
import org.easymock.MockControl;
import com.diasparsoftware.store.DataStoreException;
public class OrderBmpBeanLogicTest extends TestCase { private OrderBmpBeanLogic logic;
private MockControl orderStoreControl;
private OrderStore mockOrderStore;
protected void setUp() throws Exception { orderStoreControl =
MockControl.createNiceControl(OrderStore.class);
mockOrderStore = (OrderStore) orderStoreControl.getMock();
OrderStoreCommandExecuter simpleExecuter = new OrderStoreCommandExecuter() { public void execute(
OrderStore orderStore,
OrderStoreCommand orderStoreCommand, String exceptionMessage) {
orderStoreCommand.execute(orderStore);
} };
logic =
new OrderBmpBeanLogic(mockOrderStore, simpleExecuter);
}
public void testFindByPrimaryKey_Found() throws Exception { mockOrderStore.exists(new Integer(762));
orderStoreControl.setReturnValue(true);
Listing A.18 Testing the POJO for orders
670 APPENDIX A Complete solutions
orderStoreControl.replay();
assertEquals(
new Integer(762),
logic.ejbFindByPrimaryKey(new Integer(762)));
orderStoreControl.verify();
}
public void testFindByPrimaryKey_NotFound() throws Exception { mockOrderStore.exists(new Integer(762));
orderStoreControl.setReturnValue(false);
orderStoreControl.replay();
try {
logic.ejbFindByPrimaryKey(new Integer(762));
fail("Found object?");
}
catch (ObjectNotFoundException expected) { }
orderStoreControl.verify();
}
public void testLoad() throws Exception { Integer orderId = new Integer(762);
OrderRow orderRow = new OrderRow(orderId, "jbrains");
mockOrderStore.findByOrderId(orderId);
orderStoreControl.setReturnValue(orderRow);
orderStoreControl.replay();
logic.ejbLoad(orderId);
assertEquals(orderRow, logic.getOrderRow());
orderStoreControl.verify();
}
public void testLoad_DataStoreException() throws Exception { Integer orderId = new Integer(762);
OrderRow orderRow = new OrderRow(orderId, "jbrains");
mockOrderStore.findByOrderId(orderId);
DataStoreException exception = new DataStoreException(
"Unable to find order", new SQLException());
orderStoreControl.setThrowable(exception);
orderStoreControl.replay();
try {
logic.ejbLoad(orderId);
fail("Should have thrown an exception");
TE AM FL Y
Team-Fly®
671 Test a BMP entity bean
}
catch (DataStoreException expected) { assertSame(exception, expected);
}
orderStoreControl.verify();
} }
Of course, we need to test the command executers in isolation; fortunately, that is easy to do. First we need to verify that the EJB-based command executer does its job of turning DataStoreExceptions into EJBExceptions.
package junit.cookbook.coffee.model.ejb.test;
import javax.ejb.EJBException;
import junit.cookbook.coffee.data.*;
import junit.cookbook.coffee.model.ejb.OrderStoreCommandExecuterEjbImpl;
import junit.framework.TestCase;
import org.easymock.MockControl;
import com.diasparsoftware.store.DataStoreException;
public class OrderStoreCommandExecuterEjbImplTest extends TestCase { private MockControl orderStoreControl;
private OrderStore mockStore;
public void testStoreThrowsException() throws Exception { final DataStoreException cause = new DataStoreException();
OrderStoreCommand crashTestDummyCommand = new OrderStoreCommand() {
public void execute(OrderStore orderStore) { throw cause;
} };
orderStoreControl =
MockControl.createNiceControl(OrderStore.class);
mockStore = (OrderStore) orderStoreControl.getMock();
String failureMessage = "I expect failure";
OrderStoreCommandExecuterEjbImpl executer = new OrderStoreCommandExecuterEjbImpl(
new SimpleOrderStoreCommandExecuter());
try {
Listing A.19 OrderStoreCommandExecuterEjbImplTest