Getting Started with Java EE Tests

Java EE support is a new feature of Pax Exam 3.0.0.M1. The implementation is bound to change before the final 3.0.0 release.

Writing a test

Write a JUnit test using any of the usual JUnit 4.x annotations and any of the Java EE 6 dependency injection annotations like @Inject, @EJB, @Resource, @PersistenceContext and so on. Since Pax Exam tests run in an embedded Java EE container, dependency injection will happen automatically. No need to look up your EJB under test from JNDI.

Example:

@RunWith( PaxExam.class )
public class AuthorTest
{

    @Inject
    private LibraryService service;

    @Test
    public void byAuthor()
    {
        List<Book> books = service.findBooksByAuthor( "Mann" );
        assertEquals( 1, books.size() );

        Book book = books.get( 0 );
        assertEquals( "Buddenbrooks", book.getTitle() );
    }

    @Test
    @Transactional
    public void numAuthors()
    {
        assertEquals( 2, service.getNumAuthors() );
        service.createAuthor( "Theodor", "Storm" );
        assertEquals( 3, service.getNumAuthors() );
    }
}

In this example, LibraryService is a stateless EJB interacting with a persistence unit modelling a library with Author and Book entities.

When running this test with Pax Exam, the PaxExam JUnit runner starts a Java EE server embedded in the current Java VM, builds a WAR file on the fly containing all classes and libraries visible on the classpath (excluding Pax Exam and the embedded container itself) and deploys this WAR. The WAR contains a TestRunnerServlet which can be triggered by an HTTP GET request to run a given test method within the embedded container.

Instead of directly executing test methods, the PaxExam runner delegates the execution via HTTP to the TestRunnerServlet within the embedded container. The serialized test results are sent back in the HTTP response.

All of this is completely transparent to the outer JUnit runner (e.g. the JUnit launcher of Eclipse, or the Surefire Maven plugin).

Thus, working with Eclipse, you can easily run a whole set of Pax Exam tests by simply selecting a Java package with test classes and then launching JUnit from the context menu.

Running a Test

  • Make sure that a Java EE 6 server is installed on your local machine. The server installation directory will be passed to Pax Exam by means of a configuration property.
  • Make sure that the server is suitably configured for your system under test. Define all the data sources, connection pools or JNDI resources and copy any additional server-level libraries (e.g. JDBC drivers) to the appropriate locations.
  • Make sure that all dependencies of your system under test and all required Pax Exam modules are on the classpath (see Pax Exam Maven Dependencies).
  • To redirect java.util.logging messages from GlassFish to the SLF4J API used by Pax Exam, copy this file to src/test/resources/logging.properties.
  • Create a configuration file exam.properties in the root of your classpath with the following contents:
    pax.exam.system = javaee
    pax.exam.server.home = /opt/glassfish-3.1.2
    
    The pax.exam.server.home property defines the installation directory of your Java EE server.
  • Run your test or a whole set of tests just like any plain old JUnit test, e.g. from your IDE, under Maven Surefire or as part of your Continuous Integration job.

Transactions and Automatic Rollback

The standard rules for declarative container-managed transactions also apply to Pax Exam tests. By default, every public EJB method will run in a transaction. Thus, when calling EJB methods from your tests which modify persistent entities, these modifications will be persisted.

This is undesirable in most cases: no assumptions should be made on the execution order of tests, and all tests should run on an empty or pre-populated database with fixed contents.

Pax Exam provides a @Transactional annotation for test methods to wrap the annotated method in a UserTransaction which will be rolled back after executing the test method. This approach has the desired effect for all container managed transactions with the default transaction type REQUIRED: The container does not need to open a transaction for the EJB method, because a transaction is already open, nor does it have to commit or rollback the transaction, because it does not own the transaction.

Annotating a test class with @Transactional is a short-hand equivalent to annotating each test method.

If you are familiar with the Spring Test Context, you will notice the similarity of this transactional behaviour.

The drawback of this approach is that the transaction boundaries in your test system and in your production system are not the same, so you may get false positives or false negatives. Omitting the @Transactional annotation, your test will modify the database, so you will have to make sure that the database will be restored to a well-known state for each test run, e.g. by means of a JUnit rule.

Next Steps

Read more about the supported Java EE Containers.