Intro
I am currently working on a private project implementing a web application using GWT as frontend and EJB3 as backend technology.Testing my EJB3 beans using JUnit4 turned out to be very complicated. I had to manually inject all the required resources like EntityManager, EntityManagerFactory and all EJB3 beans the bean under test uses.
This may work for a simple project with a few beans but this will definitely not work for a huge project.
So I was looking for a better and less complicated way to test my beans.
Embedded Glassfish came to my mind, but this project isn't final yet, so I stumbled across a blog entry of Adam Bien's blog where he talks about OpenEJB and I thought just give it a try.
I use Maven as build system for all my projects so everything below this line just fits for Maven. If you use Ant or something different you may have to do other things to set up your testing environment.
Maven configuration
Using the OpenEJB embedded container is very easy. Just add the following dependency to your POM:<dependency>
<groupId>org.apache.openejb</groupId>
<artifactId>openejb-ejbd</artifactId>
<version>3.1.1</version>
<scope>test</scope>
</dependency>
In my case it turns out that the dependency activeio-core version 3.0.0-incubator is not hosted in any publicly available maven repository (well at least not in those I use). So I grabbed it from here and pushed it into my running Nexus instance.
In case you use OpenJPA as your persistence provider you have to configure the Maven surefire plugin to use the OpenEJB Java Agent. This is described in detail on this OpenEJB site.
Necessary files
My project consists of three maven modules:- project-entities (contains all JPA entities and the production persistence.xml under src/main/resources/META-INF)
- project-api (contains all session beans and services)
- project-webapp (the GWT web application)
Because I only work with annotations this file is nearly empty in my case. It just contains the following line:
<ejb-jar/>
In case you have configured a persistence.xml file under src/test/resource/META-INF of your module under test (in my case project-api) using RESOURCE_LOCAL I recommend to delete this file completely. In my case this file was interfering with the OpenEJB setup and all my beans were not deployed into the embedded container. Hence, my embedded tests were not working.
Configuring the OpenEJB embedded container for testing
Now it's time to configure the OpenEJB embedded container. This can be done in two ways. I will only explain the configuration in a JUnit base test class, so this can be used by all unit tests deriving from this class. There is another way in configuring the container using an XML file. Please consult the OpenEJB website for further details.Like I said before, I am using Hibernate as persistence provider. The setup of the embedded container is done in a method annotated with the JUnit annotation @Before, so it get's called before a test is executed. You may wish to change this in a way it better fits into your environment (using @BeforeClass, etc ...)
@Before
public void initializeEmbeddedContainer() throws Exception {
Properties properties = new Properties();
properties.setProperty(Context.INITIAL_CONTEXT_FACTORY,
"org.apache.openejb.client.LocalInitialContextFactory");
....
....
....
context = new InitialContext(properties);
}
In the above code snippet the bold lines are important. They are responsible for starting the embedded container. There are a few more properties you have to set to get the embedded tests running. Now you have to override the settings you made in your persistence.xml for running in OpenEJB.
The following lines will do that for you:
properties.put("puName", "new://Resource?type=DataSource");
properties.put("puName.JdbcDriver", "org.hsqldb.jdbcDriver");
properties
.put("puName.JdbcUrl", "jdbc:hsqldb:mem:testdatabase");
properties.put("puName.hibernate.dialect",
"org.hibernate.dialect.HSQLDialect");
properties.put("puName.hibernate.hbm2ddl.auto", "update");
properties.put("puName.hibernate.show_sql", "true");
properties.put("puName.hibernate.format_sql", "true");
puName stands for persistence unitname. Please replace this with the real name of your persistence unit. The lines in bold are necessary. I am using HSQL DB, but this should work with any JDBC database. Don't forget to let Hibernate create the DB schema for you, otherwise your tests will fail because of non existent tables.
There is just another configuration property you may want to use: Restarting the OpenEJB embedded container between tests to avoid strange side effects in your tests. This is done by setting the property:
properties.put("openejb.embedded.initialcontext.close", "destroy");
But be aware that this property is an undocumented feature and may change in further versions of OpenEJB. This Jira entry recommends to achieve the same effect in configuring the maven surefire plugin.
So the complete method to initialize your in-container tests will look like this:
@Before
public void initializeEmbeddedContainer() throws Exception {
Properties properties = new Properties();
properties.setProperty(Context.INITIAL_CONTEXT_FACTORY,
"org.apache.openejb.client.LocalInitialContextFactory");
properties.put("puName", "new://Resource?type=DataSource");
properties.put("puName.JdbcDriver", "org.hsqldb.jdbcDriver");
properties
.put("puName.JdbcUrl", "jdbc:hsqldb:mem:testdatabase");
properties.put("puName.hibernate.dialect",
"org.hibernate.dialect.HSQLDialect");
properties.put("puName.hibernate.hbm2ddl.auto", "update");
context = new InitialContext(properties);
}
Setting up a unit test
The last thing to do now is getting your beans from the embedded container. This can be done in two ways. One way is to use a special OpenEJB annotation to inject the bean into your test class. As this is an elegant way I prefer to code this with plain old Java method calls using JNDI lookup, because the first solution will make your code depend on OpenEJB API.This is how it works with local EJBs:
IServiceLocal localService = (IServiceLocal) getContext().lookup(ServiceImplLocal);
where IServiceLocal is the local interface of the session bean and ServiceImplLocal is the JNDI name of the bean implementation. The OpenEJB standard JNDI name for beans is {deploymentId}{interfaceType.annotationName}.
The deploymentId is the name of the EJB implementation (in our case ServiceImpl) and the interfaceType is Local, because the EJB under test is a local EJB.
Running your tests
Simply callmvn clean test