Java EE 6 has been a topic here at
CTP for quite a while now. During summer I had the pleasure of conducting an internship which was targeted to explore the possibilities of the upcoming standard, namely
EJB 3.1,
JPA 2.0,
JAX-RS,
JSF 2.0 and
JCDI on top of
GlassFish v3 - and it turned out surprisingly productive, although all implementations were far from being in a release state!
Only one thing we failed to run in a stable way: The
new EJB 3.1 container API refused to run our unit tests. The main problem was JPA support - persistence units got not recognized and EJBs usually failed with a
NullPointerException
calling the
EntityManager
.
Alexis and
Adam recently blogged about this specific feature, and of course I had to get back and try it out myself! Indeed, starting up a container and looking up EJBs works fine. But to test your
Entities there are still a few tweaks you might want to apply.
Start with creating a Maven project and add the
embeddable GlassFish dependency. I'm also using
TestNG instead of JUnit, as it has a nice
@BeforeSuite
annotation which allows starting the container only once before running your tests.
<dependency>
<groupId>org.glassfish.extras</groupId>
<artifactId>glassfish-embedded-all</artifactId>
<version>3.0-b69</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<version>5.9</version>
<scope>test</scope>
<classifier>jdk15</classifier>
</dependency>
As described in Alexis' blog, you can start your
EJBContainer
with a reference to your
GlassFish domain. This will allow you to start up data sources you most probably need to properly test your JPA code.
The disadvantage here is that you either depend on a hardcoded location or a system property which each of your team members have to set. Or, in case of your
continuous integration system, you might not want to have a GlassFish installation at all.
Fortunately you can create a mini GlassFish domain with only a few files. The image below shows the files you need and how I placed them in my Maven module:
You can take your
existing domain.xml
containing your data sources and place it in here - you can reference it now relatively to your module location. Your unit tests then start with:
private static Context ctx;
private static EJBContainer container;
@BeforeSuite
public static void createContainer() {
Map<String, Object> properties = new HashMap<String, Object>();
properties.put(EJBContainer.MODULES, new File("target/classes");
properties.put("org.glassfish.ejb.embedded.glassfish.installation.root",
"./src/test/glassfish");
container = EJBContainer.createEJBContainer(properties);
ctx = container.getContext();
}
This will run your unit tests against your development database. In case you want to run them in a
local database, you can simple replace the connection pool config in your
domain.xml
, e.g. with a local Derby installation:
<jdbc-connection-pool datasource-classname="org.apache.derby.jdbc.EmbeddedDataSource"
res-type="javax.sql.DataSource" name="[your DS name]" ping="true">
<property name="ConnectionAttributes" value="create=true" />
<property name="DatabaseName" value="./target/unit-test" />
<property name="Password" value="" />
<property name="User" value="" />
</jdbc-connection-pool>
This creates the database in your
target
folder and requires adding Derby to your Maven POM:
<dependency>
<groupId>org.apache.derby</groupId>
<artifactId>derby</artifactId>
<version>10.5.3.0_1</version>
<scope>test</scope>
</dependency>
Unfortunately this setup might not match with the configuration in your
persistence.xml
and generate invalid SQL for your test database. You can either solve this with
Maven filters in different profiles, or alternatively
create a staging directory for your
EJBContainer
. I'm using the
Apache Commons IO tools here for convenience:
...
private static final String MODULE_NAME = "embedded";
private static final String TARGET_DIR = "target/" + MODULE_NAME;
@BeforeSuite
public static void createContainer() throws Exception {
File target = prepareModuleDirectory();
Map<String, Object> properties = new HashMap<String, Object>();
properties.put(EJBContainer.MODULES, target);
properties.put("org.glassfish.ejb.embedded.glassfish.installation.root",
"./src/test/glassfish");
...
}
private static File prepareModuleDirectory() throws IOException {
File result = new File(TARGET_DIR);
FileUtils.copyDirectory(new File("target/classes"), result);
FileUtils.copyFile(new File("target/test-classes/META-INF/persistence.xml"),
new File(TARGET_DIR + "/META-INF/persistence.xml"));
return result;
}
You can use the
@AfterSuite
annotation to clean up the temporary folder. Note that with this setup, the
EJB lookups change:
protected <T> T lookupBy(Class<T> type) throws NamingException {
return (T) ctx.lookup("java:global/" + MODULE_NAME + "/"
+ type.getSimpleName());
}