This post is an addition to the post Unit Test Your DB Schema and Named Queries. It shows how to use the TestNG Annotations in order to simulate a lightweight EJB container enabling you to test your persistence layer (DAO, entity classes and similar). If you are using JBoss 6 you may use the embedded EB container. EJB3Unit supports testing Enterprise Beans, too. But I couldn’t get the latter to run smoothly and I do not use the former, yet. So if you are still tied to the old Java EE 5 world you might benefit form this solution.
So, the setup is like mentioned in the post this one is based on. There is only little coding effort to add in order to get the test up and running. The beans you test should inject the entity manager and other persistence layer beans only to ensure this works. Add an empty constructor to your beans for the EJB container and a constructor providing a parameter for every injected field. In your test you can pass the created entity manager and the necessary other persistence layer beans to the constructor. Afterwards you can create some tests:
public class PersistenceLayerTest { private static EntityManager manager; private static EntityManagerFactory factory; private static EntityTransaction currTrans; private static Customer currCust; @BeforeClass public static void setupManager() { factory = Persistence .createEntityManagerFactory("MYAPP_DB"); assertNotNull(factory); manager = factory.createEntityManager(); assertNotNull(manager); } @BeforeMethod public void beginTransaction() { currTrans = manager.getTransaction(); currTrans.begin(); } @Test public void prepareState() { currCust = new Customer(); currCust.setName("Johannes Neubauer"); manager.persist(currCust); final Set<Product> products = currCust.getProducts(); for(int i = 0; i < 500; i++) { final Product product = new Product(); product.setName("prod_" + i); products.add(product); } } @Test(dependsOnMethods = { "prepareState" }) public void retrieveProducts() { manager.merge(currCust); Query query = manager.createNamedQuery("de.jn.sandbox.jpa.Customer.getProductsByCustomer"); query.setParameter("cust", currCust); @SuppressWarnings("unchecked") List<Product> result = query.getResultList(); assertEquals(result.size(), 500); System.out.println(result); } @AfterMethod public void commitTransaction() { try { if(currTrans.getRollbackOnly()) { currTrans.rollback(); } else if(currTrans.isActive()) { currTrans.commit(); } } catch(Exception e) { if(currTrans.isActive()) { currTrans.rollback(); } fail("Exception occured", e); } } @AfterClass public void closeManager() { manager.close(); factory.close(); } }
The methods annotated with @BeforeClass
and @AfterClass
are called before the first test has started or after the last test has been executed respectively. These methods initialize the entity manager before the first test and closes them after the last test has finished. Before every test method (annotated with @Test
) the method beginTransaction()
is executed and afterwards the method commitTransaction()
. This simulates the transaction attribute REQUIRES_NEW for every test case.
Remember that we are using a HSQLDB here, so it might behave different than the database you use in a productive setup. Especially, the transaction isolation level of your productive db server might not be supported by the in memory db.