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.
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.
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.