Mockrunner can be used in conjunction with MockEJB to write tests for EJB based applications. MockEJB usually works with a real database. You can use the JDBC framework of Mockrunner instead of a database. There's an EJBTestModule which utilizes the MockEJB API and provides a few additional methods. You can work without using the EJBTestModule, but it makes life easier, if you use MockEJB in conjunction with Mockrunner.

This page provides a very simple example how to use the Struts test framework and the JDBCTestModule of Mockrunner in conjunction with MockEJB and the EJBTestModule. The release comes with a more complex example.

The following action takes a message parameter from the request and writes it to a database using a session bean called LogSessionBean.


public class LogAction extends Action
{
    public ActionForward execute(ActionMapping mapping,
                                 ActionForm form,
                                 HttpServletRequest request,
                                 HttpServletResponse response) 
                                 throws Exception
    {
        String message = request.getParameter("message");
        InitialContext initialContext = new InitialContext();
        Object home = 
           initialContext.lookup("com/mockrunner/example/LogSession");
        LogSessionHome logHome = 
          (LogSessionHome)PortableRemoteObject.narrow(home, 
                                                      LogSessionHome.class);
        LogSession log = logHome.create();
        log.logMessage(message);
        log.remove();
        return mapping.findForward("success");
    }
}

				

Here's the code of the LogSessionBean. The example only shows the logMessage() method. The complete bean implementation and all the other EJB stuff like the corresponding interfaces and some XDoclet tags for deploying the bean to JBoss are included in the release.


public class LogSessionBean implements SessionBean
{
    private SessionContext sessionContext;
  
    public void logMessage(String message)
    {
        Connection connection = null;
        PreparedStatement statement = null;
        try
        {
            InitialContext context = new InitialContext();
            DataSource dataSource = 
               (DataSource)context.lookup("java:comp/env/jdbc/MySQLDB");
            connection = dataSource.getConnection();
            statement = connection.prepareStatement
                                     ("insert into logtable values(?, ?)");
            statement.setTimestamp(1, 
                                   new Timestamp(System.currentTimeMillis()));
            statement.setString(2, message);
            statement.executeUpdate();
        }
        catch(Exception exc)
        {
            sessionContext.setRollbackOnly();
            throw new EJBException(exc.getMessage());
        }
        finally
        {
            try
            {
                if(null != statement) statement.close();
                if(null != connection) connection.close();
            }
            catch(SQLException sqlExc)
            {
            
            }
        }
    }
}

				

Here's the test for the above example.


public class LogActionTest extends ActionTestCaseAdapter
{
    private JDBCTestModule jdbcModule;
    private EJBTestModule ejbModule;
    
    protected void setUp() throws Exception
    {
        super.setUp();
        jdbcModule = createJDBCTestModule();
        ejbModule = createEJBTestModule();
        ejbModule.setInterfacePackage("com.mockrunner.example.ejb.interfaces");
        ejbModule.deploySessionBean("com/mockrunner/example/LogSession", 
                                    LogSessionBean.class, 
                                    TransactionPolicy.REQUIRED);
        ejbModule.bindToContext("java:comp/env/jdbc/MySQLDB", 
                  getJDBCMockObjectFactory().getMockDataSource());
    }

    public void testLogActionSuccess()
    {
        addRequestParameter("message", "testmessage");
        actionPerform(LogAction.class);
        jdbcModule.verifySQLStatementExecuted("insert into logtable");
        jdbcModule.verifyPreparedStatementParameter("insert into logtable", 
                                                    2, "testmessage");
        ejbModule.verifyNotMarkedForRollback();
        ejbModule.verifyCommitted();
        jdbcModule.verifyAllStatementsClosed();
        jdbcModule.verifyConnectionClosed();
        verifyNoActionErrors();
        verifyForward("success");
    }
}

				

The example uses several test modules in conjunction. The easiest way to do this is to extend ActionTestCaseAdapter instead of BasicActionTestCaseAdapter and to create the other test modules using createJDBCTestModule() and createEJBTestModule().

The EJBTestModule performs the MockEJB setup. It sets the MockContextFactory as default initial context factory and creates a MockUserTransaction. You have to deploy your bean to the mock container. Mockrunner allows you to set the package with the EJB interfaces, so you do not have to provide all the interfaces when deploying the beans. You can also deploy the bean directly to the mock container of MockEJB.

In this example we use the simulated database environment of Mockrunner, so we don't need a real transaction. But we want to keep track if the transaction was committed or rolled back, so we use the MockUserTransaction implementation of Mockrunner.

You can use the MockEJB feature to work with the remote container and you can get a real transaction from the JTA implementation of your application server. You have to call

MockContextFactory.
    setDelegateEnvironment((new InitialContext()).getEnvironment());
				

in the setUp() method before calling super.setUp().

Mockrunner will automatically work with a JTA transaction in this case. Refer to the MockEJB documentation for a detailed description how to setup the remote container.

Please note that you have to use the verify methods of the EJBTestModule when testing the state of the MockUserTransaction, because the MockUserTransaction is a simulated JTA transaction. If the tested code manages the transaction on its own using JDBC, you have to work with the transaction methods of JDBCTestModule. Refer to the JDBC examples.