Overview

Testing an EntityManager that uses the default, PersistenceContextType.TRANSACTION, can be challenging due to Entities detaching around transaction boundaries. If you were to take the Injection of EntityManager Example which uses an EXTENDED persistence context and switch it to a TRANSACTION persistence context making no other changes, you'd find that the test would fail. This would be because of the detach.

Generally, when using an EntityManager with TRANSACTION persistence context, your transaction should surround your use of the Entities themselves, not just the use of the EntityManager. Using a transaction in your unit test can make testing these scenarios possible.

This example shows use of @PersistenceContext to have an EntityManager with an TRANSACTION persistence context injected into a @Stateful bean using the @TransactionAttribute annotation and a TestCase that runs test code in a JTA Transaction. An EJB 3 @Entity bean is used with the EntityManager to create, persist and merge data to a database.

The source for this example is in the "testing-transactions" directory located in the openejb-examples.zip available on the download page.

The Code

{snippet:id=code|url=openejb3/examples/testing-transactions/src/main/java/org/superbiz/injection/tx/MoviesImpl.java|lang=java}

In the above bean code we see that the transaction attribute for all method of the bean has been changed the default, which is TransactionAttributeType.REQUIRED, to TransactionAttributeType.MANDATORY. The MANDATORY transaction attribute is similar to REQUIRED in that the bean method is guaranteed to be executed in a transaction, but with the added restriction that if a transaction isn't started by the client before calling the method, an exception will be thrown.

The benefit of MANDATORY in this example is that it is impossible for the client to get detached Entity issues. The client either has a transaction and therefore gets Entities which are still attached and persistent, or the client would get an exception stating that the use of a transaction is mandatory.

See the Transaction Annotations page for a full description of the available transaction attributes.

Writing a unit test for the example

The magic in the TestCase below is the TransactionBean @Stateless bean which is tucked away as an inner class of the TestCase itself. With this bean, we can call our test code within the scope of a container controlled transaction. This allows our test code to use the EntityManager and the Entities in the scope of a transaction, avoid any detach issues and satisfying the TransactionAttributeType.MANDATORY requirement of our MoviesImpl @Stateful bean.

{snippet:id=code|url=openejb3/examples/testing-transactions/src/test/java/org/superbiz/injection/tx/MoviesTest.java|lang=java}

Curious on the InitialContext parameters used? See the Injection of DataSource Example for an explanation of how any Resource can be configured via properties in the TestCase itself or via an openejb.xml file.

Running

Running the example is fairly simple. In the "testing-transactions" directory of the examples zip, just run:

$ mvn clean install

Which should create output like the following.

------------------------------------------------------- T E S T S ------------------------------------------------------- Running org.superbiz.injection.tx.MoviesTest Apache OpenEJB 3.0 build: 20080408-04:13 http://openejb.apache.org/ INFO - openejb.home = /Users/dblevins/work/openejb-3.0/examples/testing-transactions INFO - openejb.base = /Users/dblevins/work/openejb-3.0/examples/testing-transactions INFO - Configuring Service(id=Default Security Service, type=SecurityService, provider-id=Default Security Service) INFO - Configuring Service(id=Default Transaction Manager, type=TransactionManager, provider-id=Default Transaction Manager) INFO - Configuring Service(id=movieDatabaseUnmanaged, type=Resource, provider-id=Default JDBC Database) INFO - Configuring Service(id=movieDatabase, type=Resource, provider-id=Default JDBC Database) INFO - Configuring Service(id=Default JDK 1.3 ProxyFactory, type=ProxyFactory, provider-id=Default JDK 1.3 ProxyFactory) INFO - Found EjbModule in classpath: /Users/dblevins/work/openejb-3.0/examples/testing-transactions/target/test-classes INFO - Found EjbModule in classpath: /Users/dblevins/work/openejb-3.0/examples/testing-transactions/target/classes INFO - Configuring app: /Users/dblevins/work/openejb-3.0/examples/testing-transactions/target/test-classes INFO - Configuring Service(id=Default Stateless Container, type=Container, provider-id=Default Stateless Container) INFO - Auto-creating a container for bean TransactionBean: Container(type=STATELESS, id=Default Stateless Container) INFO - Loaded Module: /Users/dblevins/work/openejb-3.0/examples/testing-transactions/target/test-classes INFO - Configuring app: /Users/dblevins/work/openejb-3.0/examples/testing-transactions/target/classes INFO - Configuring Service(id=Default Stateful Container, type=Container, provider-id=Default Stateful Container) INFO - Auto-creating a container for bean Movies: Container(type=STATEFUL, id=Default Stateful Container) INFO - Configuring PersistenceUnit(name=movie-unit) INFO - Loaded Module: /Users/dblevins/work/openejb-3.0/examples/testing-transactions/target/classes INFO - Assembling app: /Users/dblevins/work/openejb-3.0/examples/testing-transactions/target/test-classes INFO - Jndi(name=TransactionBeanLocal) --> Ejb(deployment-id=TransactionBean) INFO - Created Ejb(deployment-id=TransactionBean, ejb-name=TransactionBean, container=Default Stateless Container) INFO - Deployed Application(path=/Users/dblevins/work/openejb-3.0/examples/testing-transactions/target/test-classes) INFO - Assembling app: /Users/dblevins/work/openejb-3.0/examples/testing-transactions/target/classes INFO - PersistenceUnit(name=movie-unit, provider=org.apache.openjpa.persistence.PersistenceProviderImpl) ERROR - JAVA AGENT NOT INSTALLED. The JPA Persistence Provider requested installation of a ClassFileTransformer which requires a JavaAgent. See http://openejb.apache.org/3.0/javaagent.html INFO - Jndi(name=MoviesLocal) --> Ejb(deployment-id=Movies) INFO - Created Ejb(deployment-id=Movies, ejb-name=Movies, container=Default Stateful Container) INFO - Deployed Application(path=/Users/dblevins/work/openejb-3.0/examples/testing-transactions/target/classes) Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 2.25 sec Results : Tests run: 2, Failures: 0, Errors: 0, Skipped: 0
  • No labels