Getting Started with Java EE Tests
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 tosrc/test/resources/logging.properties
.
- Create a configuration file
exam.properties
in the root of your classpath with the following contents:Thepax.exam.system = javaee pax.exam.server.home = /opt/glassfish-3.1.2
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.