Showing posts with label maven. Show all posts
Showing posts with label maven. Show all posts

Sunday, March 04, 2007

Testing EJB3 (JPA) with Maven2

This tutorial describes how to test EJB3 beans as a part of a Maven2 build, i.e. without an EJB container but with an in-memory, transactional database. I make the following assumptions:
  • JDK 1.5
  • EJB 3.0, i.e. Java Persistence API (JPA) 1.0
  • Maven 2 (2.0.5)
I don't assume much more than that. Our application contains no explicit references to Spring or Hibernate as EJB3 itself borrows much of their goodness. We avoid all the XML config usually associated with Spring/Hibernate by using the JPA persistence annotations in our entity beans. To facilitate testing, any injected dependency references, e.g. @PersistenceContext or @EJB, are declared package access so that the JUnit test classes can set them appropriately.

As with many enterprise applications, much of our business logic is embedded within database queries, most in JPQL, and some can be quite complex. It's vital that we're able to test them outside of a container as a part of our normal, continuously-integrated build. For that, we use the Hypersonic (hsqldb) in-memory database. We'll need a JPA implementation, of course, and because we deploy to JBoss 4.0 in production, which uses Hibernate's JPA, we choose the same. Of course, any certified JPA implementation should work just as well.

pom.xml

So our first step is to declare our testing dependencies in our POM:

<!-- For in-memory sql testing -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>3.2.0.ga</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>hsqldb</groupId>
<artifactId>hsqldb</artifactId>
<version>1.8.0.7</version>
<scope>test</scope>
</dependency>

Aside: I toyed with using the new H2 database instead of Hypersonic, but the version I tested, 1.0.20061217, included only table locks, causing my app to deadlock. Hypersonic supports row locking.

Although not strictly necessary, I recommend adding this to your POM, too:

<testResources>
<testResource>
<directory>src/test/resources</directory>
<filtering>true</filtering>
</testResource>
</testResources>

You'll see why in the next section.

Note that we didn't need to change the POM much. In particular, no special configuration of the Surefire plugin (the testcase invoker) is required.

src/test/resources/META-INF/persistence.xml

All JPA apps need a persistence.xml file. Your app will likely already have one beneath src/main/resources. Both will be in your CLASSPATH when the tests are run. The JPA will aggregate them, so you need to make sure all your persistence-units, both real and test, are uniquely named. How you organize your persistence-units is up to you. It's probably easiest to have just one for testing, or you might, for example, prefer to create one per test class.

Here's what I recommend:

<persistence>
<persistence-unit name="hibernate-hsqldb">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<jar-file>${project.build.directory}/classes</jar-file>
<properties>
<property name="hibernate.dialect" value="org.hibernate.dialect.HSQLDialect"/>
<property name="hibernate.connection.driver_class" value="org.hsqldb.jdbcDriver"/>
<property name="hibernate.connection.url" value="jdbc:hsqldb:."/>
<property name="hibernate.connection.username" value="sa"/>
<property name="hibernate.connection.password" value=""/>
<property name="hibernate.hbm2ddl.auto" value="create-drop"/>
</properties>
</persistence-unit>
</persistence>

Note the jar-file element. The ${...} expansion only works when filtering is turned on in the testResources element of the POM (see above). By setting the jar-file this way, we're telling the persistence provider to find our entities by searching for persistence annotations in our classes. From these, the required DDL is generated and the schema is created. Alternatively, you could use one or more class elements instead. This would allow you finer-grained control over which classes and/or packages are involved in a test. This strategy would probably also lead to multiple persistence-unit elements defined.

One thing to keep in mind about Hibernate. There's a known bug in which it won't create a schema before creating tables for your entities, so you're in trouble if you've set the schema attribute in your @Table annotations. I recommend removing this attribute from your entities anyway: it's a violation of the DRY principle. A better solution is to set the hibernate.default_schema property in your persistence-unit.

One debugging tip: you may want to include the following property in your persistence-unit:

<property name="hibernate.show_sql" value="${hibernate.show_sql}"/>

That way, you could add something along these lines in your POM:

<properties>
<hibernate.show_sql>false</hibernate.show_sql>
</properties>
<profiles>
<profile>
<id>debug</id>
<properties>
<hibernate.show_sql>true</hibernate.show_sql>
</properties>
</profile>
</profiles>

So when you want to see the SQL produced by your unit tests...

$ mvn -Pdebug test

src/test/resources/import.sql

Some apps make use of Hibernate's ability to run some SQL after a schema is created. If it finds a file named import.sql on the classpath, it'll run the SQL within it. This can be a problem if, for example, your import.sql is exploiting Oracle PL/SQL commands that wouldn't make any sense to Hypersonic. Fortunately, you can easily solve the problem by simply putting a different -- possibly empty -- import.sql file beneath src/test/resources, because Hibernate will find that one first in the CLASSPATH.

Aside: I experienced some problems with a test-specific import.sql file, strange stack traces generated by Hibernate's SchemaExport class dumped to stdout without actually failing the tests. I ended up truncating src/test/resources/import.sql completely.

src/test/java/org/yours/SomeTest.java

Speaking of unit tests, here's an obviously-contrived example:

package org.yours;

import javax.persistence.*;
import junit.framework.*;
import org.apache.log4j.Logger;

public class SomeTest extends TestCase
{
public void testService() throws Exception
{
log.info ("testService");
EntityManager em = emf.createEntityManager();
TestData.build (em);
ServiceBean slsb = new ServiceBean();
slsb.em = em;
assertTrue (slsb.something());
}
protected void setUp() throws Exception
{
log.debug ("setUp");
emf = Persistence.createEntityManagerFactory ("hibernate-hsqldb");
}
protected void tearDown() throws Exception
{
log.debug ("tearDown");
emf.close();
}
private Logger log = Logger.getLogger(getClass());
private EntityManagerFactory emf;
}

There is a lot of room for artistic freedom within this structure, but the essential point is the create/close of the EntityManagerFactory in the setUp/tearDown methods. This provides a clean database to each testXXX method.

If your transactional requirements are minimal, you may also want to create/close an EntityManager member variable in setUp/tearDown, too.

If your seed data requirements are complex, you may want to look into something like DBUnit, but in my experience, it's often easier to construct "builder" objects that can model various situations for your tests.

Transactions

For a lot of test cases, you can probably safely ignore transactions. Within one test, letting each of your beans use the same EntityManager without ever beginning or committing its transaction may go a long way toward testing your app sufficiently, especially if all of your beans are simply propagating the transaction anyway.

But transactions can get icky quickly, especially when you have multiple cooperating session beans with their transaction attribute set to REQUIRES_NEW. I'm going to show you one way of solving the problem using Java's dynamic proxies, but there's probably a more elegant solution.

Here's something I call a TransactionProxy. Others might call it "a good argument for AOP". It assumes your target bean class has an EntityManager member named em.

public class TransactionProxy implements InvocationHandler
{
private EntityManagerFactory emf;
private Object target;
private Field field;

private TransactionProxy (Object target, EntityManagerFactory emf)
{
this.emf = emf;
this.target = target;
try {
this.field = target.getClass().getDeclaredField ("em");
this.field.setAccessible (true);
} catch (Exception e) {
throw new RuntimeException (e);
}
}

public static Object newInstance (Object target, EntityManagerFactory emf)
{
return Proxy.newProxyInstance (target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new TransactionProxy (target, emf));
}

public Object invoke (Object proxy, Method m, Object[] args)
throws Throwable
{
EntityManager em = emf.createEntityManager();
try {
field.set (target, em);
em.getTransaction().begin();
Object result = m.invoke (target, args);
em.getTransaction().commit();
return result;
} catch (InvocationTargetException e) {
em.getTransaction().rollback();
throw e.getTargetException();
} catch (Exception e) {
throw new RuntimeException (e);
} finally {
em.close();
}
}
}

The idea is that you create it with an instance of say, a stateless session bean (SLSB) and an EntityManagerFactory, and subsequent invocations on the proxy will wrap calls to the real bean inside a transaction obtained from the EntityManagerFactory. For example:

Service service = (Service) TransactionProxy.newInstance (new ServiceBean(), emf);

This allows you to combine your beans in any number of transactional contexts. But it's not optimal since it's not actually using the @TransactionAttribute annotation of the classes under test. Hopefully, that's what the more elegant solution mentioned above is doing.

I'll update this tutorial after I've confirmed that. Until then, happy hacking!

Sunday, February 18, 2007

Hating Maven less than Ant

I've been writing Java code since 1996 and struggling with build tools at least as long. Ant is the most comprehensive and Maven is the most ambitious. I hate both of them, mostly because they each require me to describe my project's build requirements using XML, which should never be used as a programming language, though often it's used as a configuration file format, but because all configuration files ultimately will evolve into programming languages... I digress.

For different reasons, Ant and Maven are both terrific and terrible at once. Ant allows you to describe your build however you please, but you must effectively do it from scratch for each project. Maven encourages many best practices by providing a lot of build features out-of-the-box, adopting a sort of "convention over configuration" philosophy popularized by the Ruby on Rails project, among others.


So for my money, Maven is the logical choice for a typical Java project. Unfortunately, few Java projects are typical, especially few enterprise Java projects. And for those projects, Maven can be frustrating because, like a child or a turtle, it's difficult to make it do something it doesn't want to do.

For example, let's say I'm debugging a build and I want to echo the value of a property to the console during a build. With Ant, I could add a target to build.xml:

<project>
<target name="debug">
<echo>The value is ${some.property}</echo>
</target>
</project>

With Maven 1, I can invoke Ant tasks within a goal defined in maven.xml:

<project>
<goal name="debug">
<echo>The value is ${some.property}</echo>
</goal>
</project>

Maven 1 provides easy access to Ant tasks, as you can see. Unfortunately, doing anything non-trivial with Maven 1 involves Jelly, which is pure "executable XML hell". Maven 2 did away with Jelly, but...

With Maven 2, it's bewilderingly complicated to echo a simple property to the console. You must include the "antrun" plugin in your pom.xml, configure an execution to run Ant's echo task, and bind that execution to a particular phase of your build (but see comments for other options). Ready? Here we go:

<project>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-antrun-plugin</artifactId>
<executions>
<execution>
<id>debug</id>
<phase>validate</phase>
<configuration>
<tasks>
<echo>The value is ${some.property}</echo>
</tasks>
</configuration>
<goals>
<goal>run</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

Note that our choice of the "validate" phase is completely arbitrary, but in order for our task to execute, it must be bound to some phase. Unlike our Ant and Maven 1 examples above, Maven 2 provides no way to invoke a single task without invoking all other tasks bound to the same phase along with all those before it. Maven 2 introduced the concept of profiles to address this perceived limitation, but I'll save that discussion for a future blog post.

Clearly, the Maven designers have violated a fundamental tenet: "the solution cannot be more abstract than the problem!" In trying to define and encourage best practices, Maven unapologetically sacrifices simplicity and flexibility, arguably two of the most important qualities of any software build tool, even for those projects exhibiting all accepted best practices.

Somewhat surprisingly, I generally still think it's worthwhile to convert your Ant projects to Maven, no matter how difficult. Maven's pain is concentrated, sort of like filling a cavity early in the project, resulting in a relatively pain-free existence once your project leaves the dentist's office. Ant is more like a minor toothache annoying you for the entire duration of your project, and then spreading to the other projects in your mouth as each begins its life with a copy of some other's build.xml file. Yes, of course it's simpler to do simple things with Ant, but adding enough of those simple things to projects over time can (and will, in my experience) lead to a pretty darn complex, brittle, and incomprehensible build.

In the long run, I think you'll end up hating Maven less than Ant, too. But if you insist on using Ant, use Ivy to manage your dependencies.

UPDATED Maven 2 example, removing unnecessary <property> element to reflect Eric's comment.