No results found

Your search did not match any results.

We suggest you try the following to help find what you’re looking for:

  • Check the spelling of your keyword search.
  • Use synonyms for the keyword you typed, for example, try “application” instead of “software.”
  • Try one of the popular searches shown below.
  • Start a new search.
Trending Questions
 

Streamlining Your EJB Tests With MockEJB

Abstract

Enterprise JavaBeans (EJBs) are an important building block in a J2EE application and can give you a standard framework that allows service definition, event-driven processing, and object-relational persistence. However, a common complaint from those using EJBs is that their use makes unit testing of applications much more difficult. EJBs rely on the services of a container in order to run, but deploying beans into a container before unit testing them slows the process down and makes debugging more complicated. The recent, and much welcomed, rise in popularity of test-driven development has exacerbated this problem for many developers because of the rapid cycle of writing tests, writing production code, and refactoring that the approach implies.

This article introduces a library, MockEJB, which provides one possible solution to the problems of testing EJBs by allowing them to be tested inside or outside the EJB container. Built on top of existing mock object technology, MockEJB allows developers to develop and unit test their EJBs as plain Java objects (outside the EJB container) before later deploying them to the container for integration testing. Once in the container, you can use MockEJB to thoroughly test EJBs by controlling the beans' environment and allowing unexpected conditions to be simulated. The use of MockEJB can simultaneously make developers lives easier and more productive, while helping to achieve much more thorough test coverage than is often the case.

The EJB Testing Challenge

Enterprise JavaBeans (EJBs) are key building blocks in many large J2EE applications, and are used to define package services and define their interfaces (using session beans), create event handlers (with message-driven beans), and sometimes provide a persistence mechanism (via entity beans). Much of EJBs' power and benefit comes from the standardized runtime environment and services that the EJB container provides, including automatic thread and memory management, transaction management, and declarative security. However, because of this reliance on the container, EJBs cannot run outside it, and so this powerful runtime environment also causes problems when we come to unit test EJB components. How do we easily unit test something running inside the EJB container provided by an application server?

A number of approaches have been proposed for EJB testing, from simple EJB base classes that allow basic unit testing outside the container, to sophisticated test frameworks such as Cactus (see the Additional Reading section), which make it possible to run unit tests on EJBs running inside the container. More recently, the "Mock Objects" approach has emerged as an alternative way of unit testing code with complex environmental requirements, and MockEJB is an EJB-specific development of this idea.

Test-Driven Development and Mock Objects

Before diving into the details of MockEJB, it is worth recapping the background ideas and technologies it is based on.

Test-driven development (TDD) is an approach to software development (rather than testing) that places unit testing at the center of the process. Rather than writing large amounts of code and then doing as much unit testing as time allows, TDD turns the process on its head and only writes code in order to achieve a successful test. The process is highly iterative, involving a rapid cycle of writing a test, then writing the code required to make the test pass, and finally refactoring to improve the design if the new code means that the design is now less than optimal. The basic rules of TDD are that you write as many tests as possible, that all unit tests are automated, and that all tests must pass all the time. The Additional Reading section has some links to further information about TDD.

Although it's a simple idea, TDD has been found to be incredibly effective in practice, because the ever-growing unit test set acts to provide immediate feedback and validation of the code being written so that unpleasant surprises are not saved up to cause problems late in the development process. A later benefit of TDD is that it results in a large, reliable set of unit tests, which means developers can return to their code after a long period away from it (or indeed adopt other developers' code) and change it safely, relying on the unit tests to fail if an error is made during the changes.

One of the implications of achieving an effective TDD process is that developers need to use a development environment that allows them to write and execute tests very quickly against a constantly changing code base. The problem from the point of view of an EJB developer is that the deployment process required to allow an EJB to run in the container slows the compilation process down considerably and makes the development environment rather more complex. This can result in the dangerous situation of EJB developers having to "batch up" their tests and running larger sets of unit tests against the code less regularly. While still allowing unit testing, this reduces the speed of the feedback loop that the TDD approach relies upon. Therefore, to support TDD effectively, we would like to be able to eliminate the deployment cycle for our EJBs while they are being developed; using a "mock" EJB container is one of the ways we can achieve this.

Another challenge when using TDD for the development of a complex system is the difficulty of isolating the functionality under test from the rest of the system. Application components (usually packaged as session beans) normally rely on other components to provide services to them. In the unit test environment, we want to eliminate the dependencies of the component under test so that the services called return predictable test data instead of running the actual production code. Dependency on persistent data (represented by entity beans) is another manifestation of this problem. It may be desirable to develop the unit test in such a way that it does not depend on a database (which, even in a development environment, is often a shared resource, and is therefore difficult to control).

Mock objects (or "mocks") are a technology that can help you to replace complex or difficult-to-control runtime components with lightweight, controllable versions that are more amenable to a rapid unit test process. The idea of mock objects is to allow a unit test to replace part of the normal runtime environment with a specially written replacement, which implements the same interface as the regular component but can be easily controlled by the unit test code. Mock object implementations exist for parts of the java.sql JDBC classes, java.io IO library, java.net networking library, and many other pieces of the standard Java runtime environment. Such mock implementations allow us to simulate errors, test database code without the need to have a database present, test network code without needing a real server (or client), and so on. In short, mock objects make the unit testing process much more tractable for code that requires a complex runtime environment. MockEJB is a recent addition to the mock objects landscape, providing mock implementations of many of the important J2EE interfaces.

Introducing MockEJB

MockEJB is an EJB testing framework written by Alexander Ananiev. It combines a number of existing ideas and technologies that give the EJB developer a powerful testing framework for easy unit testing of EJBs, both inside and outside the EJB container. In essence, MockEJB provides mock object implementations of all the important J2EE interfaces required by an EJB and therefore provides a mock container implementation that developers can easily control from their unit tests. The mock container it provides is a plain Java object and allows EJBs to be tested as plain Java objects, outside the traditional container of a J2EE application server. However, MockEJB also integrates with the Cactus server-side testing framework and provides the EJB developer with a unit test environment inside the application server, if required.

This article focuses on the use of MockEJB for testing beans outside the application server. When used in this way, some of the key features that MockEJB provides include:

  • Automatic generation of the EJBObject implementing your interfaces, calling your bean class.
  • Provision of the bean environment, including container transactions, EJBContext objects, and EJBMetaData.
  • An in-memory JNDI provider, allowing the binding and lookup of container resources.
  • Invocation of operations on other EJBs.
  • Support for CMP and BMP entity beans.
  • An in-memory JMS provider, allowing JMS messages to be sent and received.
  • The ability to add interceptors around the bean to allow control and monitoring of the test environment.

MockEJB is supplied as a Java library, with samples and javadoc documentation to guide its use. Like most libraries, the easiest way to come to grips with MockEJB is to use it to solve some simple problems, and so in the next section, we'll see how MockEJB can help you test some session beans.

Using MockEJB

To illustrate the use of MockEJB, we have developed a very simple application containing two session beans and one MDB. The zip file associated with this article contains a simple codeline comprising the source code for the beans under test, the unit test source code, and an Ant build script to build and test the beans. To build the code and run the tests, you will need to install Ant, JUnit, and MockEJB, as well as the JDK. You can get full instructions for using the example code in the README file within the codeline.

The EJBs provided have been developed to allow you to investigate some specific features of MockEJB, and are all extremely simple, providing a basic calculator via various service interfaces. Three beans are included:

  • SimpleCalc is the simplest bean, a stateless session bean offering a remote interface providing addition, subtraction, multiplication, and division services for double operands. This bean allows you to investigate MockEJB's support for simple EJB testing.
  • ParsingCalc is another stateless session bean that offers a single calculate(String expression) operation. This bean parses the string expression passed to it to extract the operator and operands for the operation required, and calls the SimpleCalc bean to perform the calculations. This bean allows you to investigate MockEJB's support for inter-bean references.
  • MessageCalc is a message-driven bean that listens for requests via a JMS queue, extracts the body of any text messages received, and uses the ParsingCalc bean to evaluate them, returning the result of the calculation to the JMSReplyTo destination of the request message. This bean allows you to investigate MockEJB's support for JMS and MDB.

The general process for using MockEJB to test your beans is as follows:

  • Call MockEJB to install itself as the default JNDI provider in the current environment (this simply involves calling the static method MockContextFactory.setAsInitial()).
  • Create a JNDI context and mock EJB container instance associated with it (this is simply a case of creating instances of InitialContext and MockContainer).
  • Optionally, create any mock J2EE environment objects required by your beans (for example, JMS factories and destinations) and install them into the JNDI directory ( for example, by creating objects from the com.mockejb.jms package and calling context.rebind() to associate them with a name in the mock JNDI directory).
  • Create deployment descriptors for your beans to describe them to the framework. MockEJB deployment descriptors are Java objects, rather than XML documents, and are created by constructing objects of the right class (such as SessionBeanDescriptor) with constructor parameters that define the interfaces and bean instance to deploy.
  • Finally, the mock container's deploy() method can be called for each deployment descriptor object to deploy the beans and make them available for testing.

Once this simple process is complete, the beans can be accessed via the standard J2EE lookup/narrow/invoke process that you are already familiar with.

Testing simple EJBs

To illustrate the process, let's start with the simplest possible example and examine how MockEJB allows us to test a simple independent session bean outside the container. The code fragment below shows how the bean is deployed to a local in-memory container for testing:


					  // Setup the JNDI environment to use the MockEJB 
// context factory
MockContextFactory.setAsInitial();
        
// Create the initial context that will be used for binding EJBs
Context ctx = new InitialContext();
        
// Create an instance of the MockContainer
MockContainer mc = new MockContainer(ctx);

// Create deployment descriptor for our sample bean
// This is used instead of an XML descriptor
SessionBeanDescriptor dd = new SessionBeanDescriptor(
   "java:comp/env/ejb/SimpleCalc", 
   SimpleCalcHome.class, SimpleCalc.class,
   new SimpleCalcBean()) ;
    
// Deploy our bean to the container allowing it to
// be found via JNDI and tested
mc.deploy(dd);

This code has set up the environment to use MockEJB's mock JNDI implementation, rather than a container-based one, create a mock EJB container to deploy our bean to, and then deploy the bean to the container by describing it via a SessionBeanDescriptor object. This use of a Java object as a bean deployment descriptor is the main difference that you'll notice between MockEJB's container and a real application server EJB container. As you can see from the code, creating the descriptor for a bean is simple and just involves constructing it by specifying the JNDI name it should be bound to, the classes that provide the home and remote interface definitions, and a bean class instance to deploy.

Once the bean has been deployed to our mock container, we can write tests against it as if it were deployed to a conventional application server container. The JUnit test method below shows the code needed to call the business methods of our sample bean:

public void testTwoPlusTwo() throws Exception
{
    // Look up the home
    Context ctx = new InitialContext();
    Object ejbObj = 
        ctx.lookup("java:comp/env/ejb/SimpleCalc");
    
    // Narrowing is not needed with MockEJB or WebLogic, but 
    // we call it anyway for standardization
    SimpleCalcHome home = (SimpleCalcHome)
       PortableRemoteObject.
          narrow(ejbObj, SimpleCalcHome.class);
    SimpleCalc calc = home.create();
    assertEquals("Add operator failure", 4.0, 
                  calc.add(2.0, 2.0), 0.0) ;
}

As you can see, this code is identical to the code required to invoke operations on the bean if it were running within an application server container, and so no MockEJB-specific programming is required once the test beans have been deployed. In the sample code associated with the article, the TestSimpleCalcBean JUnit test case class contains this example code for testing the SimpleCalc bean. However, when looking at the sample code, note that that code to perform the MockEJB-specific initialization of the container and deployment of the beans has been factored out of the JUnit test case class into an abstract base class called MockBeanTestBase. This class gives you additional features (such as running inside the EJB container) and avoids duplication of code between tests.