◆ Problem
You want to test a session bean method inside a real target container.
◆ Background
There are a number of reasons why this might be necessary, including the possibil- ity that your application server supports a particular feature or extension, or has a particular defect that you need to take into account during testing. We refuse to name names, but life can be interesting moving from application server to applica- tion server, and it is possible to uncover production code “defects”—even when they merely expose platform defects—running against a live container that you might not find through simulation. You might also want to execute performance tests, the results of which depend on being executed in a real environment.11 If you need to test your session bean in a real container, then this recipe can help you do it.
◆ Recipe
Put simply, use Cactus (http://jakarta.apache.org/cactus). Cactus is a JUnit-based test framework that executes tests in a J2EE application server. More than that, Cactus allows you to pretend that you are executing tests on the client side—that is, outside the application server—by executing them transparently on the server side. It is an evolutionary step beyond the idea of simply executing server-side tests on the server (see the Discussion section for more). Let us return to our Coffee Shop application and the ShopcartOperations session bean. Recall that this ses- sion bean performs shopcart-oriented operations, such as adding items to it. We want to test adding a single coffee product to an empty shopcart. With Cactus, the test is straightforward—almost indistinguishable from a plain-vanilla JUnit test case. We show the test in listing 11.6.12
package junit.cookbook.coffee.model.ejb.test;
import java.util.Vector;
import javax.naming.*;
11You can execute performance tests outside the application server; however, you would then only be able to interpret the results relative to one another. Those results offer limited feedback.
Listing 11.6 A Cactus test for adding a product to a shopcart
12This test requires Cactus on the class path. We recommend adding the entire contents of the Cactus lib directory, just to be safe.
395 Test a session bean method
in a real container
import javax.rmi.PortableRemoteObject;
import junit.cookbook.coffee.model.CoffeeQuantity;
import junit.cookbook.coffee.model.ejb.*;
import junit.cookbook.coffee.model.ejb.ShopcartOperationsHome;
import org.apache.cactus.ServletTestCase;
public class AddToShopcartTest extends ServletTestCase { public void testEmptyShopcart() throws Exception { Context context = new InitialContext();
Object homeAsObject = context.lookup("ejb/ShopcartOperations");
ShopcartOperationsHome home =
(ShopcartOperationsHome) PortableRemoteObject.narrow(
homeAsObject,
ShopcartOperationsHome.class);
Vector requestedCoffeeQuantities = new Vector() { {
add(new CoffeeQuantity(2, "Sumatra"));
} };
ShopcartOperations shopcartOperations = home.create();
shopcartOperations.addToShopcart(requestedCoffeeQuantities);
Vector items = shopcartOperations.getShopcartItems();
assertEquals(1, items.size());
assertEquals(new CoffeeQuantity(2, "Sumatra"), items.get(0));
} }
The only real difference between this test and a regular JUnit test is that this test case class extends Cactus’s ServletTestCase, rather than junit.framework.Test- Case. The class ServletTestCase provides the transparent server-side test execu- tion service, so you only need to write your test, deploy it into the application server, and then execute the tests from any test runner. Cactus transparently dele- gates executing the test to a server-side component—at least it is transparent when your test passes. When your test fails, you receive an “Internal Error” mes- sage, HTTP Status Code 500, followed by a description of the problem that occurred on the server. Aside from this extra error message, though, test failure reporting occurs as it does when executing tests locally. Cactus is truly a wonderful tool for server-side testing.
We recommend deploying your tests in a separate J2EE application (*.ear file) so that they remain isolated from your production web application resources, and are not subject to any unnecessary security policies. This simplifies the test environment Use Cactus’s server- side TestCase class
396 CHAPTER 11
Testing Enterprise JavaBeans
considerably. In addition, you should read all about the integration between Cac- tus and Ant on the Cactus web site. If you are using Ant to build your product and run your tests, then it is easy to integrate Cactus into that environment.
◆ Discussion
This technique helps you cope with having to test session beans in a real con- tainer. If you can, we recommend refactoring the session bean instead. Extract the business logic and test it outside the container, as we describe in recipe 11.1.
We recognize that you are not always able to do this, but if you can, then you should. Your next alternative is to simulate the container, as we describe in recipe 11.2, using MockEJB.
If you need to test your EJB in the container, but do not wish to use Cactus, then we suggest you write a simple servlet that executes tests on the server on your behalf and reports the results as a web page. Before you run off and write that yourself, keep in mind that this is exactly how Cactus began, so we recommend you simply use it. Still, if you find that Cactus is more than you need, then you can start with your own, simpler solution. When you reach the point where you realize you are reimplementing Cactus, stop. There is little point in doing that.
One last comment about testing a session bean in a container: be aware of all the costs of testing within the container, because they add up. First, you have the complex test environment: you need to start and stop the application server as needed, you have to configure the application server correctly and, in many cases, you have to deal with licensing issues. Next, you have a slow test environment:
remote communications, JNDI lookups, database access; these things all add to test execution time. When you consider that the goal of Programmer Testing is to exe- cute the tests after every change to ensure you have not broken anything, it becomes obvious that these tests are simply too slow to support this goal. Perhaps the most insidious cost, though, is an indirect one: most session beans interact (eventually) with the database. Testing these session beans in a live environment means setting up test data for each and every test. We discussed in chapter 10, “Testing and JDBC,” the cost of setting up test data, whether you try setting up fresh data for each test or, worse, try to have all your tests share a common test data set. These are costs that just keep climbing throughout the lifetime of the project, and which grow superlinearly—that is, costs which accelerate. That is bad news. These are the reasons we recommend that you limit the amount of EJB testing you do in a live environment. The costs just add up far too quickly for our liking.
397 Test a CMP entity bean
◆ Related
■ 11.1—Test a session bean method outside the container
■ 11.2—Test a legacy session bean
■ Chapter 10—Testing and JDBC
■ Cactus (http://jakarta.apache.org/cactus)