Enterprise JavaBean (EJB) is an important component block in J2EE applications. It provides developers with a standard framework that supports service definition, event-driven processing, and Object-link persistence. However, developers who use ejbs often complain that the use of ejbs makes unit tests of applications more complex. EJB depends on the service of the container to run, but deploying bean to the container before the unit test slows down the process and makes debugging more complex. This problem has been aggravated by the recent popularity of test-driven development. This is mainly due to the quick week of writing tests, writing production code, and refactoring included in this method.
This article introduces a framework, mockejb, which allows testing EJB inside or outside the EJB container, thus providing a possible solution to the EJB test problem. Mockejb built on the existing mock object technology allows developers to deploy ejbs to containers for integration testing, such) in the same way as a common Java object, EJB is enabled and tested in units. In containers, you can use mockejb to fully test the EJB by controlling the bean environment and simulating unexpected conditions. The use of mockejb not only makes the developer's life easier, but also increases their productivity, and also helps achieve more comprehensive test coverage than usual.
Challenges faced by EJB Testing
Enterprise JavaBean (EJB) is an important component block in many large J2EE applications. It is also used to define the packaging service and its interfaces (using session bean), create an event handler (using message-driven bean), and sometimes use it to provide a persistence mechanism (through the entity bean ). Many features and advantages of EJB come from the standardized runtime environment and services provided by the EJB container, including automated thread and memory management, transaction management, and declarative security. However, EJB cannot run outside the container because of its dependency on the container. Therefore, when you perform a unit test on the EJB component, this powerful runtime environment also has some problems. How can we easily perform unit tests on things provided by an application server and running in an EJB container?
Many methods have been proposed for EJB testing, from simple EJB base classes that support basic off-container unit tests to complex testing frameworks, for example, cactus (see the additional Reading Section) makes it possible to perform unit tests on ejbs running in the container. Recently, as an optional solution for unit testing of code with complex environment requirements, the method of "imitating objects" emerged. mockejb is the idea specific to the implementation of EJB development.
Test-driven development and imitation objects
Before introducing the details of mockejb, it is necessary to describe the basic ideas and technologies for it.
Test-driven development (TDD) is a software development (rather than testing) method that places unit testing at the center of the process. Unlike the traditional method of writing a large amount of code and then conducting as many unit tests as possible within the allowed time range, TDD completely subverts this process.OnlyWrite code for successful tests. The process is highly iterative, including a fast cycle:Write Test, And thenWriteRequired to make the test successfulCode, And finallyReconstructionTo improve the design-if the new Code shows that the design is not optimal. The basic rule of TDD is to write as many tests as possible. All unit tests are automated and all tests must always pass. For more information about TDD, see section.
Although the idea is very simple, it is found that TDD is incredibly effective in practice. As the growing unit test set provides direct feedback and verification for the written code, hidden troubles will not accumulate and cause problems later in the development process. Another advantage of TDD is that it produces a reliable large unit test set, which means that developers can leave the code (or actually use the code of another developer) after a period of time, return to the Code and make changes securely, because if an error occurs in the change, the unit test will fail.
To implement an effective TDD process, the development environment used by developers must allow them to quickly write and execute tests for a constantly changing code base. From the perspective of EJB developers, the problem is that the deployment process that allows EJB to run in the container greatly slows down the compilation process and makes the development environment more complex. This will cause the following dangerous situation: EJB developers have to "Batch Process" their tests and do not often run large unit test sets on code. Although the unit test is still carried out, this reduces the fast feedback speed that the TDD method is proud. Therefore, in order to support TDD more effectively, we hope to eliminate the EJB deployment cycle during development. Using the "Imitation" EJB container is an implementation method.
Another challenge for developing complex systems using TDD is that it is difficult to separate the functions to be tested from other parts of the system. Application Components (usually packaged as session beans) generally rely on other components to provide services to them. In the unit test environment, we hope to eliminate the dependencies of the components to be tested so that the called service can return predictable test data, rather than running the actual production code. The dependency on persistent data (represented by the Entity Bean) is another embodiment of this issue. It is best to perform unit tests in a way that does not depend on the database (even in the development environment, the database usually shares resources, so it is difficult to control ).
The Imitation object (or "mocks") technology helps replace complex or uncontrollable runtime components with lightweight and controllable versions that can generate fast unit testing processes. The idea of imitating objects is to allow unit tests to replace parts of the normal runtime environment with a specially written alternative. The alternative implements the same interface as the normal component, but it is easier to control unit test code. The implementation of the simulated object exists in the Java. SQL JDBC class, java. Io library, java.net network library, and many other standard Java Runtime Environment components. These imitation implementations allow us to simulate errors, test database code without having to have a database, test network code without having to have a real server (or client), and so on. All in all, imitating objects makes it easier to process unit tests of code that requires complex runtime environments. Mockejb is a new framework in the object field. It provides many important J2EE interfaces.
Introduction to mockejb
Mockejb is an EJB test framework written by Alexander aniev. It combines many existing ideas and technologies and provides a powerful testing framework for EJB developers so that they can easily perform single-element tests on ejbs, whether it is inside or outside the EJB container. In essence, mockejb provides the imitation object implementation of all the important J2EE interfaces required by EJB. Therefore, it provides developers with an imitation container implementation that can be easily controlled in a single-element test. The Imitation container provided by it is a common Java object, and it allows testing of ejbs outside the traditional container of the J2EE application server just like a common Java object. In addition, mockejb is integrated with the cactus server-side testing framework, providing a unit testing environment for EJB developers.
This article focuses on using mockejb to test beans outside the application server. Mockejb provides the following important features:
- Implement automatic generation of ejbobject of the interface and Bean class.
- Automatic Configuration of bean environment, including container transactions, ejbcontext objects, and ejbmetadata.
- A jndi provider in memory that allows binding and searching for container resources.
- Call other EJB operations.
- Supports CMP and BMP object beans.
- A jms provider in memory that allows sending and receiving JMS messages.
- Add an interceptor around the bean to allow control and monitoring of the test environment.
Mockejb is provided as a Java library. It also provides examples of its usage and javadoc documents. Like most libraries, the easiest way to master mockejb is to use it to solve some simple problems. So next we will look at how to use mockejb to test some session beans.
Use mockejb
To illustrate the use of mockejb, we have developed a very simple application that contains two session beans and one MDB. The zip file included in this article contains a simple codeline, which contains the source code of the bean to be tested, the unit test source code, and an ant build script for building and testing beans. To build code and run the test, you must install ant, JUnit, mockejb, and JDK. In the README file of codeline, you can find detailed instructions on using the sample code.
The EJB is developed to study some specific functions of mockejb. They are very simple and provide a basic calculator through various service interfaces. There are three beans:
- Simplecalc, the simplest bean, is a stateless session bean that provides an interface for adding, subtraction, multiplication, and Division services for double operands. This bean will help you understand the support of mockejb for simple EJB testing.
- Parsingcalc is also a stateless session bean that provides only one calculate (string expression) operation. This bean analyzes the string expression passed to it, extracts the operators and operands of the required operations, and CALLS simplecalc bean for computation. This bean will help you understand the support of mockejb for inter-bean reference.
- Messagec1c, a message-driven bean that listens to messages through the JMS queue, extracts the body of any received text message, and analyzes it using parsingcalc bean, return the calculated result to the jmsreplyto destination site of the request message. This bean will help you understand the support of mockejb for JMS and MDB.
The general process of testing bean with mockejb is as follows:
- Call mockejb to install it as the default JNDI provider of the current environment (you only need to call the static method mockcontextfactory. setasinitial ()).
- Create a JNDI context and simulate the associated EJB container instance (you only need to create the initialcontext and mockcontainer instances ).
- (Optional) create any objects that mimic the J2EE environment (for example, the JMS factory and the destination site) required by the bean and install them in the JNDI directory (for example,. mockejb. create an object in the JMS package and call context. rebind () to associate them with the names under the directory that imitates JNDI ).
- Create deployment descriptors for beans to describe them to the framework. Mockejb deployment descriptor is a Java object instead of an XML document, and is created by a constructor of an appropriate class (such as sessionbeandescriptor). The constructor parameters define interfaces and bean instances to be deployed.
- Finally, the deploy () method that imitates the container can call all deployment descriptor objects so that the bean can be deployed and tested.
After this simple process is completed, you can access the bean through the standard J2EE search/Contract/call process that you are familiar.
Test simple EJB
To illustrate this process, we will start with the simplest possible examples to learn how mockejb allows us to test simple standalone session beans outside the container. The following code snippet shows how the bean is deployed to a container in the local memory 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 sets up an environment that uses mockejb to simulate the JNDI implementation, instead of a container-based environment. It creates an EJB container to deploy the bean and then describes the bean through the sessionbeandescriptor object, deploy it to a container. This usage of using Java objects as bean deployment descriptors is the main difference between the mockejb container and the real application server EJB container. It can be seen from the code that it is very simple to create a descriptor for bean, you only need to specify the JNDI name to bind, the class defined by the local and remote interfaces, and the Bean class instance to be deployed.
After the bean is deployed on a simulated container, you can write a test for it, just as it is deployed on a conventional Application Server container. The following JUnit test method shows the Code required to call the related method of the 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) ;
}
It can be seen that if you run the code in an application server container, it is the same as the Code required to call the bean operation. Therefore, once the test bean has been deployed, you do not need mockejb-specific programming. In the sample code included in this article, the testsimplecalcbean JUnit test case class contains the sample code for testing simplecalc bean. However, you must note that the code for executing mockejb-specific container initialization and bean deployment has been restructured from the JUnit test case class into an abstract base class called mockbeantestbase. This class can provide other features (for example, running in an EJB container) and avoid code duplication between tests.
Test the EJB that references other ejbs.
The previous example is obviously too simple, because the bean itself does not use resources residing in the container, so we only need to instantiate the bean Class Object and call its method to achieve the same effect. Let's take a look at how mockejb helps us test the session beans that reference other beans. In our example code, there is a parsingcalc bean, which requires the simplecalc bean service to execute computing.
By deploying all the required beans on the imitation container and associating the correct JNDI name, mockejb makes it easy for ejbs to search for and reference each other. The corresponding sample code can be found in the testparsingcalcbean JUnit class. The key part of initialization is displayed in the following code snippet:
// Initialize initial context and container here
// ...
// Create deployment descriptor for the bean under
// test.
SessionBeanDescriptor testDD =
new SessionBeanDescriptor(
"java:comp/env/ejb/ParsingCalc",
ParsingCalcHome.class, ParsingCalc.class,
new ParsingCalcBean()) ;
// Create deployment descriptor for the bean that
// the bean under test relies upon.
SessionBeanDescriptor depDD =
new SessionBeanDescriptor(
"java:comp/env/ejb/SimpleCalc",
SimpleCalcHome.class, SimpleCalc.class,
new SimpleCalcBean()) ;
// Deploy both beans to the mock container
mc.deploy(depDD) ;
mc.deploy(testDD);
(Note: In the provided sample code, bean deployment uses the abstract base class extended by the JUnit test class.Deployremotesessionbean ()Method. In the above code snippet, we can clearly display the required process .)
Now, both beans are deployed on the imitation container, and if the JNDI name bound to simplecalc (in the above example, Java: COMP/ENV/EJB/simplecalc) match the name found in the initial context of parsingcalc bean, and mockejb returns a reference to the correct EJB object. In the testparsingcalcoperations method of the sample unit test class, you can find some sample unit test code of parsingcalc bean, where Bean is tested using conventional EJB calls. If you run the test, parsingcalc bean successfully locates and calls the simplecalc bean operation.
This example illustrates the simplest use of mockejb, but mockejb also provides a very useful function, that is, the ability to develop, test, and debug session beans outside the container. Similarly, we do not intend to take advantage of any more complex functions provided by the EJB container.
Test MDB
Now let's consider how to use the mockejb JMS function to test MDB outside the container. Obviously, the test MDB is different from the test session bean (or the entity bean) because the bean can only be used to send messages to the appropriate JMS topic or queue it is listening. Therefore, although you still need to deploy the bean itself, most of the work of setting MDB for testing is related to creating a JMS object and connecting the bean to the correct target site. Fortunately, mockejb makes it possible to implement all these tasks in just a few lines of code. The following code snippet illustrates how to set up an environment to test MDB:
// Initialize initial context and container
MockContextFactory.setAsInitial();
Context ctx = new InitialContext();
MockContainer mc = new MockContainer(ctx);
String factoryName = "jms/QueueConnectionFactory" ;
String requestQueueName = "jms/queue/CalcRequestQueue" ;
String responseQueueName = "jms/queue/CalcResponseQueue" ;
// Create the mock JMS objects and bind them to
// the appropriate names in the mock JNDI directory
QueueConnectionFactory qcf =
new QueueConnectionFactoryImpl() ;
ctx.rebind(factoryName, qcf);
Queue queue = new MockQueue(requestQueueName) ;
ctx.rebind(requestQueueName, this.queue);
ctx.rebind(responseQueueName,
new MockQueue(responseQueueName));
// Create an instance of the bean under test and
// attach it to the test queue as a listener
MessageCalcBean beanObj = new MessageCalcBean() ;
((MockQueue)queue).addMessageListener(beanObj) ;
// Create a deployment descriptor for the MDB
// and set the flag indicating that the JMS
// objects are already created and bound to the
// specified names
MDBDescriptor mdbDD = new MDBDescriptor(
factoryName, requestQueueName, beanObj) ;
mdbDD.setIsAlreadyBound(true) ;
// Deploy the MDB ready for testing
this.container.deploy(mdbDD);
From the code snippet, we can see that most of the Code for bean configuration involves creating appropriate JMS imitation objects and binding them to the names required to mimic the JNDI directory. Now, when MDB finds the queue connection factory, it will be passed to the reference created above to imitate the factory; similarly, when it requests calcrequestqueue, it will be passed a reference to the imitation team column, allowing us to manipulate and monitor the queue as needed. The sample code corresponding to this process can be found in the deployqueuemessagedrivenbean () method of the mockbeantestbase class.
Now we can create an appropriate (mimic) JMS message to send it, and then distribute it to bean by imitating the queue to test the MDB. The code snippets are as follows:
// ... following from previous code fragment
// Provide us with access to the queues
QueueConnection conn = qcf.createQueueConnection();
QueueSession sess = conn.createQueueSession( false,
Session.AUTO_ACKNOWLEDGE);
// Create our test message
TextMessage reqMsg = session.createTextMessage();
// Look up the reply queue and set in the message
Queue replyQ = (Queue)ctx.lookup(RESPONSE_QUEUE_NAME) ;
reqMsg.setJMSReplyTo(replyQ) ;
// Start the connection and create a receiver to
// to begin receiving messages
conn.start();
QueueReceiver recv = sess.createReceiver(replyQ) ;
// Get rid of any messages that might be on the
// response queue because of failed tests
while(recv.receiveNoWait()!=null) ;
// Send a message to run a unit test
reqMsg.setText("2.0 + 2.0") ;
sender.send(reqMsg);
TextMessage reply1 =
(TextMessage)recv.receive(RECEIVE_TIMEOUT_SEC) ;
assertNotNull(reply1);
assertEquals("Wrong addition result",
"2.0 + 2.0 = 4.0", reply1.getText()) ;
In the test code, we can use standard JMS APIs to find queues and connect factories, and create, send, and receive messages. Because we use the imitation queue implementation, the mockejb JMS implementation processes the messages we send to the Request queue and calls the onmessage () method of MDB. This will block until the onmessage () method returns, so we can immediately check the response. The blocking (same step) feature of mockejb's JMS implementation is very conducive to unit testing, because it allows us to test the result of the JMS-driven operation, without the need to handle unpredictable latency and timeout in unit test code. It also allows us to perform integration tests on the areas where the Session Bean and MDB are connected together.
In addition to the implementation of standard JMS APIs, mockejb also provides a variety of convenient methods to send messages and check the content of the queue, without turning to the sometimes troublesome jms api. In this example, we do not use these methods, because this method has a disadvantage, that is, when running EJB in a real EJB container, the test cannot be reused, the sample code provided in this Article can run unit tests in both modes.
To sum up, in a few short code snippets, we use mockejb to deploy the Session Bean and MDB to a container in the local memory and bind the bean to the JNDI directory, to allow them to call each other and use the JMS provider in the mockejb memory to test an MDB without configuring the real JMS provider in the container. Unfortunately, more mockejb features cannot be displayed in this short article, but we hope you have seen the potential of this framework to support effective test-driven EJB development.
Other features of mockejb
Although we cannot demonstrate all the features of mockejb, We can briefly discuss other features that have not yet been discussed in this rich functional framework.Test Entity Bean
In addition to providing many convenient functions for testing session beans and message-driven beans, mockejb also provides many convenient functions for testing BMP and CMP Entity beans outside the container. The Imitation container provides an in-memory Entity Bean database that can be filled with test data to provide predictable tests without a database. The Processing Method of BMP object bean is similar to Session Bean, because Bean implementation occurs, although this implementation is generated for CMP object bean at runtime. Mockejb also provides its own implementation (AOP terminology here), allowing installation to provide specific actions on the finder and container management relationships (CMR.
Transactions using mockrunner and JDBC
To test the transaction behavior of the Cross bean, mockejb can be used with the mockusertransaction Implementation of the J2EE usertransaction interface of mockrunner. An instance that imitates a user transaction can be bound to the javax. transaction. usertransaction name of mockejb's imitation JNDI directory, and then the transaction object can be manipulated and monitored to check the correctness of the behavior. Similarly, the JDBC Implementation of mockrunner can be used in mockejb to provide a data source that imitates the data source. The data source provides standard test data, and the code to be tested can monitor its correctness.
AOP and interceptor
As mentioned above, mockejb provides a simple AOP framework that allows us to insert code at any point in the EJB processing lifecycle. In mockejbAspectIs definedInterceptorAndEntry PointThe interceptor specifies the logic that will run before and after the target method is called (that is, the "surround notification" In AOP terminology), and the entry point defines the set of methods to be intercepted. The provided implementation is very strong and flexible. It allows the introduction of interceptor in any method call in the framework (or bean or test code) and monitoring testing (for example, checking whether a method is called) or insert data (for example, a primary key list is returned from the BMP finder method ).
In fact, AOP provides a powerful optional solution for Imitating object methods, because the interceptor allows you to change the behavior of a single method of the target class, instead of re-implementing the entire class as an imitation object. For mockejb, these two methods can be exchanged. For example, you can create an imitation version of the entire session bean (for example, using the easymock framework), or just intercept and change the method selected in the bean.
Use mockejb in a container
Finally, we have mentioned a mockejb feature, but it is not described in detail, that is, the capabilities used in conventional EJB containers. This usage of mockejb can provide beans with an easy-to-control runtime wrapper, allowing them to simulate conditions that are difficult to create in regular unit tests, this makes the EJB unit test in the container easier.
Running EJB outside the container allows you to develop code using the TDD method, but it cannot completely replace in-container testing. Mockejb does not support XML-based deployment descriptors (not to mention vendor-specific descriptors and configuration files ), therefore, deploying ejbs to containers and running unit tests are an important part of verifying their correctness. When mockejb is used, most of the development work of developers can rely on the external container mode, and testing in containers is less frequent-for example, before submitting changes to the version control system.
To implement container support, mockejb provides an extension to the cactus servlettestcase class called optionalcactustestcase, it allows you to run tests in the test case subclass outside the server under cactus on the server or the container using mockejb. If you want to run the test under cactus on the server, you can control the behavior of the class by setting the system attribute mockejb. Cactus. mode to true. When running, you can call the isrunningonserver () method to change the behavior between the two modes if needed. The container internal mode can flexibly mix and match the container with the resources provided by the imitation. This is done by the JNDI context of mockejb and can be delegated to the container context when necessary. Therefore, you can control the test boundary by imitating EJB, and other resources (such as JMS and data sources) can be provided by containers to make the test more realistic.
The sample code included in this article allows you to run the provided unit tests on beans within or outside the EJB container. Therefore, it can be seen as an example of using mockejb within the container.
Download
Mockejbsamples.zip contains the sample code in this article.
Conclusion
In this article, we reveal some problems arising from the EJB testing during the rapid development, testing, and reconstruction of TDD methods, and introduce a testing framework, mockejb. By providing a lightweight imitation EJB container that supports EJB testing in the memory, this framework can help solve these problems.
We can see that with less code (most of which can be placed in reusable base classes), we can deploy the session bean or MDB to the imitation container, and test it locally, without the need to configure and use a completely J2EE application server.
In addition, mockejb provides many other useful functions, including testing the capabilities of entity beans, support for running in containers once the initial development is completed (convenient control of bean runtime environment) and support for JDBC and J2EE transactions (through mock runner ), and a flexible and powerful aspect-Based Interceptor implementation that allows control and monitoring of the test environment.
We encourage you to use mockejb in your project to experience the streamlined unit test process provided by it, especially when you plan to use test-driven development for EJB components.
Additional reading
- Apache ant -- build tool used by the sample code
- Jakarta cactus-J2EE unit test framework in a container
- Easymock -- uses the Java proxy mechanism to dynamically generate a framework for Imitating objects
- Mockejb -- mockejb framework Homepage
- Mockobjects.com -- this site has a lot of information related to the imitation object method for unit testing.
- Mockrunner is also a J2EE imitation object framework that is used inside mockejb and provides the implementation of JDBC, JMS, and Struts imitation objects.
- Testdriven.com -- a public resource page for test-driven development
- Test driven development -- One of the earliest descriptions of the test-driven development method, written by the object mentor
Dev2dev ejbpolicycenter
Source: http://dev2dev.bea.com/pub/a/2005/10/mock_ejbs.html
Author Profile |
|
Alexander aniev has over 16 years of experience in designing and developing computer systems using various languages and technologies. He is currently an architect in a large consulting company. |
|
Eoin Woods has been working in the enterprise IT field for 15 years and is currently an architect at a London-based Global Investment Bank. He has been using BEA products since tuxedo version 4. |