This section covers advanced EJB testing practices, goes over some pitfalls and how to avoid them, identifies OpenEJB's glass jaw, and outlines a future course for the EJB testing community. Here is our list of topics:
- Configuring multiple EJB Containers
- Isolating component subsets
- Auto-generate the
- Auto-validate your enterprise component JARs
- Testing against a full-blown EJB Server
- Mock object techniques with real components
- Testing thread safety
- The ultimate integration test scenario
- Is there a Maven plugin?
Configuring multiple EJB Containers
The sample application you downloaded earlier includes a single container configuration file; the
conf/openejb.conf file. The contents of the
openejb.conf file should look like this:
<!-- conf/openejb.conf --> <openejb> <Container id="Default BMP Container" ctype="BMP_ENTITY" /> <Container id="Default Stateful Container" ctype="STATEFUL" /> <Container id="Default Stateless Container" ctype="STATELESS" /> <Connector id="Default JDBC Database"> JdbcDriver org.axiondb.jdbc.AxionDriver JdbcUrl jdbc:axiondb:DefaultDatabase:target/test-database </Connector> <Deployments dir="target/" /> </openejb>
The OpenEJB test framework uses the system properties to configure the container, allowing you to create multiple container configurations in your Maven build and run them by switching the system property in the command line:
Alternately, you can add your custom configuration properties to the
project.properties file, or you can change them on the fly in your
maven.xml file. Using this technique, you can test components against multiple different container setups. You can have as many configurations as you wantone for each time you start JUnit if you like. Everything is set up outside your testing code, so you can change it in the build without having to play property hunt-and-peck inside your test classes.
Isolating component subsets
If you only want to test a subset of your components, such as your Stateless Session Beans or the beans from a specific JAR, you can easily set up OpenEJB to do that. The following configuration file illustrates how this is done:
<!-- my tiny openejb.conf --> <?xml version="1.0"?> <openejb> <Container id="Default Stateless Container" ctype="STATELESS"/> <Deployment JAR="myStatelessEJBs.jar" /> </openejb>
This configuration would only work if you in fact had only stateless session beans in your JAR. OpenEJB will not load support for any of the other bean types, which gives it a tiny memory footprint. However, it will also not be able to load any EJB JARs that have beans of those types in them. If a JAR is found containing other bean types, it is simply not loaded when OpenEJB starts. A warning will be logged letting you know which JARs were not loaded.
Loading a portion of a JAR would cause all sorts of problems, and is not possible. Imagine what would happen if one of the loaded beans tried to access one of the beans that hadn't been loaded.
Notice the singular
<Deployment ...> tag above. This differs from the
<Deployments ...> tag we looked at before. It lets you specify each JAR to load into the container by name. You can have as many (or as few)
<Deployment.../> tags as you like and therefore load as many individual EJB JARs as you like.
More information about configuring your OpenEJB Container can be found at OpenEJB's project site.
src/java/META-INF directory contains the standard
ejb-jar.xml deployment descriptor, which is used by the container at runtime. However, behind the scenes another vendor-specific deployment descriptor, the
openejb-jar.xml file is being automatically generated in memory and used to help OpenEJB work with your EJB JAR.
Many testing scenarios and EJB applications will not require you to write your own
openejb-jar.xml file, but there are a couple of cases that would require your direct intervention:
- If you have a CMP with finder methods, you need to manually supply your own openejb-jar.xml file. There is no way to generate intelligent select methods via reflection. All the generators can see at runtime are methods like findFoo(java.lang.String, java.lang.String), and this doesn't provide enough context to build intelligent select methods.
- If any bean has more than one
resource-ref, you'll again need to write your own
openejb-jar.xmlfile. There's no way to automatically generate the information describing which Connectors goes to which
OpenEJB also has a feature in the works (not yet implemented) that will look at an existing
openejb-jar.xml file and use it for all defaults when creating a new
openejb-jar.xml. This would mean even if you had finders and multiple resource refs in one bean, you could keep a partially complete
openejb-jar.xml and leave out the values which could be auto-generated. Things like adding and removing finders or
resource-ref tags would require you to generate a real
openejb-jar.xml, but you would be able to get by with auto-generation most of the time.
More information about writing the
openejb-jar.xml file can be found at http://www.openejb.org/deploy.html.
Auto-validate your enterprise component JARs
Using OpenEJB's auto validation tool is simple, and can save you a lot of time and headache during development. In each
maven.xml file of the EJB-testing-examples you've been using, there's a
validate goal. Right now we've got it set up to run directly after the enterprise component JAR is assembled and before the tests are run (before the JAR is loaded into the EJB Container).
validate goal looks for an
openejb.validate.output property, which determines how much content to log and which accepts the following values (the hyphen must be included):
- -v Not too verboserecommended for screen output.
- -vv Somewhat verbose.
- -vvv Very verboserecommended for file output.
- -xml Can be written to a file and parsed by UI tools.
This validate tool is too neat not to show in action. I did the folling things to purposely mess up my beans:
- Misstyped the ejb-class name of the stateless session bean
- Commented out the getSalary business method
- Commented out the matching ejbPostCreate for first set of create methods
- Commented out the ejbCreate method for the second set of create methods
- Added a new ejbCreate method that isn't used in the home interface
And here is the validator output:
C:ejb-testing-examples-part02> maven __ __ | / |__ _Apache__ ___ | |/| / _` V / -_) ' ~ intelligent projects ~ |_| |___,_|_/___|_||_| v. 1.0-rc1-SNAPSHOT Attempting to download openejb-core-1.0-SNAPSHOT.jar. default: java:prepare-filesystem: java:compile: [echo] Compiling to C:ejb-testing-examples-part02example_02/target/classes java:jar-resources: test:prepare-filesystem: test:test-resources: test:compile: test:test: jar:jar: jar: validate: [java] ------------------------------------------ [java] JAR C:ejb-testing-examples-part02example_02/target/ejb-testing-examples-1.0.jar [java] [java] FAIL ... nrfx/examplebean: Missing class [java] FAIL ... Employee: No such business method [java] FAIL ... Employee: Create method not implemented. [java] FAIL ... Employee: No ejbPostCreate method [java] WARN ... Employee: Unused ejbCreate method [java] [java] For more details, use the -vvv option BUILD FAILED File...... file:/C:/ejb-testing-examples-part03/example_03 Element... java Line...... 35 Column.... 29 Java returned: 1 Total time: 4 seconds
Testing against a full-blown EJB Server
Now we're getting to the heavy-duty stuff. If you want to test how your components will run in a full-blown EJB Server, you can do so. Contained in the
maven.xml file that comes along with the example application above, there are four commands which you can use to start, stop, restart, and check the status of the full OpenEJB server.
At the command line, you can type the following:
$ maven start $ maven stop $ maven restart $ maven status
These four goals can be worked into your build sequence, but should be done so with a bit of care. There are a couple of trip-ups you could encounter while using this functionality:
- If you try and delete the ejb jars when the server is started, it will fail. Can't delete jars that are currently in use.
- Make sure you build your jars before starting the server (just as with using OpenEJB embedded). If you build the jars *after* you start the server, they will have arrived too late and you'll be out of luck.
On the other hand, there are some rather helpful bits that you should also be aware of:
- Executing the
startgoal when the server is already started is ok. The words, "server already started" will appear. This not only helps when you've called the
startgoal, but also when you have an actual installation of OpenEJB or Geronimo running somewhere else on your system. and test on it without having to whip up another set of maven goals. (Starting, stopping, and deploying in Geronimo is a manual process obviously)
- Executing the stop command when the server is already stopped is ok. The words, "server not running" will just appear.
- Remembering whether your server is started or stopped can become difficult when you write tests for fifteen hours straight (and we know you do), so we've added a
statusgoal that prints "server is stopped" or "server is started".
- We also added a
restartgoal, which comes in handy when you don't care if the server is already running, and just want to your tests again. This goal executes the stop and start commands for you with a one second pause in the middle, so if that isn't enough time for your computer (if you've got a slow CPU, for instance) you can just execute the
Firing up the full server for running your tests will allow you to test things like security, authentication, remote access, and other impossible-to-fake container functionality.
1. Download the example code for Part 3 from http://www.openejb.org/ejb-testing-examples-part03.zip.
C:>dir ejb-testing-examples-part03 Directory of C:ejb-testing-examples-part03 05/16/2004 08:09p <DIR> . 05/16/2004 08:09p <DIR> .. 05/16/2004 07:53p <DIR> example_01 05/16/2004 07:53p <DIR> example_02 05/16/2004 07:53p <DIR> example_03 0 File(s) 0 bytes 4 Dir(s) 15,507,103,744 bytes free
2. Start the server and fire off the tests
C:ejb-testing-examples-part03> maven -Dopenejb.server=Remote start default __ __ | / |__ _Apache__ ___ | |/| / _` V / -_) ' ~ intelligent projects ~ |_| |___,_|_/___|_||_| v. 1.0-rc2 Attempting to download openejb-core-1.0-SNAPSHOT.jar. OpenEJB 1.0-SNAPSHOT build: 20040516-0640 http://www.openejb.org [init] OpenEJB Remote Server ** Starting Services ** NAME IP PORT admin thread 127.0.0.1 4200 ejbd 127.0.0.1 4201 telnet 127.0.0.1 4202 ------- Ready! build:start: start: default: java:prepare-filesystem: java:compile: [echo] Compiling to c:ejb-testing-examples-part03/target/classes java:jar-resources: test:prepare-filesystem: test:test-resources: test:compile: test:test: jar:jar: aspectj:init: jar: validate: java:prepare-filesystem: java:compile: [echo] Compiling to c:ejb-testing-examples-part03/target/classes java:jar-resources: test:prepare-filesystem: test:test-resources: test:compile: test:test: [junit] Running com.nrfx.articles.openejb.EmployeeAccessorsTest [junit] Tests run: 4, Failures: 0, Errors: 0, Time elapsed: 2.223 sec [junit] Running com.nrfx.articles.openejb.EmployeeCreateTest [junit] Tests run: 2, Failures: 0, Errors: 0, Time elapsed: 1.583 sec [junit] Running com.nrfx.articles.openejb.EmployeeFindTest [junit] Tests run: 5, Failures: 0, Errors: 0, Time elapsed: 1.582 sec [junit] Running com.nrfx.articles.openejb.Example01Test [junit] Tests run: 34, Failures: 0, Errors: 0, Time elapsed: 2.844 sec [junit] Running com.nrfx.articles.openejb.Example02Test [junit] Tests run: 34, Failures: 0, Errors: 0, Time elapsed: 2.704 sec BUILD SUCCESSFUL Total time: 39 seconds
Notice that the Entity Bean tests run several times faster than they did in embedded mode. This is because we are running against the same container for all the tests and not paying the cost of embedded Axion startup on every test case. The session bean related tests are predictably slower because they didn't involve axion do begin with and because the jump across VM boundaries slows them somewhat.
Mock Object techniques with real components
Remember Mock Objects is much more than an API. Mocking is a development process. The principle of Mock Object testing is to instantiate an implementation of a bean component's public interface, declare what it's future state should be, perform an operation against it, and verify that the component's state has been altered in the manner you predicted. When testing your Session Beans in OpenEJB, you can use Mock Object testing strategies with real Entity Beans and a real EJB Container environment. You remain true to the spirit of the Mock Object development style, and true to the implementation details of your Entity components.
The same strategy applies when you're testing the Create, Read, Update and Delete (CRUD) operations of an Entity Bean. For instance, to test an update on an entity bean, you can create your object and give it its original state, decide what its end state should be, then pass it through your session components and verify that it was altered in the way you expected.
This is still the Mock Object process, even if you do it with a real component. The advantage of using this over mock object implementations or a faux-server environment is that you can also test how your session beans will behave if the update operation fails in the middle of the transaction, the session bean's reaction to a security exception when a specific user is logged on, if the session bean is thread safe, etc.
The ultimate integration test scenario
If you think you've mastered all the concepts I've covered in this series, worry that you've run out of things to test, and you're looking for a challenge, try out this integration testing scenario. Try testing your application in all of the following environment scenarios:
- Client, Embedded Container & Embedded Database in a single VM
- Client in first VM, Standalone Container & Embedded Database in second VM
- Client & Embedded Container in first VM, Standalone Database in second VM
- Client in first VM, Standalone Container in second VM & Standalone Database in third VM
This is the testing scenario that the OpenEJB team uses for their own project, so I know it's possible to implement.
Is there a Maven plugin?
There's work already in progress to build a complete OpenEJB container driven testing plugin for Maven. That plugin will be based on many of the concepts and code from the examples in this article, as well as some I've squirreled away for later, and will help automate the process of setting up and tearing down data sets more reliably, reduce your project dependencies and enable you to write multiproject builds. Look for information about this at the OpenEJB container driven testing plugin page.
So far, most of the pitfalls we've identified in this article have related to Maven or Ant-related build techniques for automating the testing process, and even as this article goes to press, those issues are being dealt with. However, there is one other factor which should be taken into consideration when testing with the OpenEJB Container. OpenEJB 1.0 is only EJB 1.1 compliant.
This is (officially) not the end of the world. If you currently develop 2.0 compliant beans or are itching to work on 2.1 compliant beans, you'll be glad to know that the OpenEJB team is quite far along with their OpenEJB 2.0 implementation. This new version will be EJB 2.1 compliant and just as embeddable as the one we use in this article.
OpenEJB 2.0 will also run as the default EJB Container in Geronimo, giving it the chance to compete with Weblogic and Websphere in a J2EE compliant production server. Someday in the not-too-distant future, the tiny EJB Container you've got embedded in your testing suite could literally be the same as the one you're using in your production server.
We should do whatever we can to test the full capacity of our Enterprise components. That means testing them as close to the metal as we can getdirectly inside the EJB Container. People who test Enterprise JavaBeans using the faux-environment approach are testing with their eyes closed. They are testing against a poorly implemented abstraction on top of a misunderstood API. No good will ever come of this practice, which grew as a reaction to the "heaviness" of EJB Servers.
OpenEJB is not heavyproblem solved. It can be as big or as small as you like. It's, performant, embeddable, robust, easy to use, spec compliant, costs nothing, and at the end of the day, it's designed to help you write better code and leverage the investment you've made in your EJB systems. The ability to easily test EJBs dramatically raises their appeal for agile developers, and lowers the learning curve that has long plagued Enterprise Java development.
Consider what you could and should test for in your enterprise systems, the things that you need to be confident on as you migrate from development to production. Now, let go of the notion that testing these things is impossible or even difficult (it isn't). Container driven testing with OpenEJB opens up a whole new world of possibilities.
Although I tried, there is no way I could do justice to every aspect of In Container Enterprise testing in this article. But I hope I was able to show you the path, and help you get started.
Here's your cake. Here's your fork. Dig in and stay tuned. There's more on the way.
About the Author
N. Alex Rupp is a professional Open Source developer and software architect for Open Technology Systems, a Twin Cities firm dedicated to helping small and medium sized companies participate in the Open Source software movement. He frequently writes code at the Dunn Brothers coffee shop in southeast Minneapolis. He is also President of the Twin Cities chapter of IASA, the International Association of Software Architects, and JSR-94 implementation lead for Drools, a dynamic rule engine project at The Codehaus.
He can be reached at firstname.lastname@example.org