Skip navigation links

Package org.wamblee.test.persistence

This package provides test library for database testing in general and JPA testing specifically.

See: Description

Package org.wamblee.test.persistence Description

This package provides test library for database testing in general and JPA testing specifically. As part of this it provides a means to transparently start an inmemory database from a junit test or connect to an external database. Also, given a persistence unit it is easy to start testing it in a junit test with only a few lines of code.

The main use cases are explained below:

See also the design overview.

Basic database testing, transparently connecting to a database

Starting the database:
      Database db = DatabaseBuilder.getDatabase();
      DataSource dataSource = db.start();
 

If nothing is specified in the user's environment, an inmemory database is started (derby). Using the datasource is just standard JDBC now.

After a test it is good practice to stop the database:

      db.stop();
 

Connecting to an external database

Connecting to an external database can be done by requiring the 'external' capability on the database provider.
      Database db = DatabaseBuilder.getDatabase(DatabaseProvider.CAPABILITY_EXTERNAL); 
 
This also requires a number of environment variables or system properties to be set, see ExternalDatabase.

However, the most convenient way to set the capabilities is usually to set a system property or environment variable see the javadocs of DatabaseBuilder. and specifically DatabaseBuilder.DB_CAPABILITIES_PROP

Executing code within a JDBC transaction

To execute code withing a JDBC transaction, use the DatabaseUtils and use the DatabaseUtils#executeInTransaction(org.wamblee.support.persistence.DatabaseUtils.JdbcUnitOfWork) method.

       DatabaseUtils dbutils = new DatabaseUtils(dataSource);
       boolean result = dbutils.executeInTransaction(
           new JdbcUnitOfWork<Boolean>() {
               @Override
               public Boolean execute(Connection aConnection) throws Exception {
                   ResultSet res = jpaTester.getDbUtils().executeQuery(
                       aConnection, GROUP_QUERY, aGroup);
                   return res.next();
               }
           });
 
DatabaseUtils also provides various other utility methods to work with JDBC queries.

Using DB Unit in your tests

To work with DBUnit, DatabaseUtils#createDbTester(org.dbunit.dataset.filter.ITableFilterSimple) must be used passing it in the tables to use in the form of a ITableFilterSimple object.

      IDatabaseTester dbtester = dbutils.createDbTester(new ITableFilterSimple() {
          public boolean accept(String aTableName) throws DataSetException {
              return aTableName.startsWith("XYZ_");
          }
      });
 

The reason for using a DatabaseUtils instead of DBUnit directly is that DatabseUtils will keep track of connections and close them when DatabaseUtils is closed

Basic JPA testing

First step is to create a PersistenceUnitDescription that matches the persistence unit you want to test.

Second step is to make sure that all entities are listed explicitly in your persistence.xml. Currently, class path scanning appears to fail when run from junit. Specifying all entities explicitly is not necessarily a bad thing as it is also more efficient.

Now create a JpaTester in your test code:

      @Before
      public void setUp() throws Exception {
 
          // First we create the JpaTester by telling us which persistence unit we
          // are going to test
          jpaTester = new JpaTester(new MyPersistenceUnit());
          jpaTester.start();     
      }
 

Then in test code execute some JPA code within a unit of work:

      jpaTester.getJpaBuilder().execute(new JpaUnitOfWork() {
          public Void execute(EntityManager aEm) {
              MyEntity entity = new MyEntity("a", "b");
              aEm.persist(entity);
              return null;
          }
      });
 

Note that in addition to this method it is also possible to directly control transactions through JpaBuilder#begin(), JpaBuilder#commit(javax.persistence.EntityManager), and JpaBuilder#rollback(javax.persistence.EntityManager).

JPA testing combined with JDBC and DBUnit

The JPATester provides access to all required object. It is usually convenient to get them directly from the JPATester after initializing it:

      builder = jpaTester.getJpaBuilder();
      dbutils = jpaTester.getDbUtils();
      dbtester = dbutils.createDbTester(new MyTables());
 

Testing a service that requires a transaction

Using TransactionProxyFactory it is possible to create a proxy for a given service interface to provide the semantics of 'requires new' transaction semantics.

      TransactionProxyFactory factory = new TransactionProxyFactory(
           jpaTester.getJpaBuilder(), Service.class);
      Service service = new ServiceImpl(factory.getTransactionScopedEntityManager()); 
      Service proxy = factory.getProxy(service);
      proxy.execute(...);
 

In the above example, the Service POJO requires an EntityManager in its constructor and it is passed a transaction scoped entitymanager from the factory. This entitymanager is in fact a so-called contextual reference. Next, the proxy is obtained from the factory. Invoking any method on it will make sure a new transaction is started and a new entity manager is created for the scope of that transaction.

Controlling transactions through a UserTransaction

It is also possible to manage JPA transaction through a UserTransaction. This is mostly useful if you are test software that uses UserTransaction for managing transactions, or if you want to manage multiple transactional resources. See the explanation at SimpleTransactionManager for more details.

Design overview

Database transparency


database

DatabaseProvider uses java.util.ServiceLoader to find all implementations of DatabaseProvider on the classpath. It then asks the database providers whether they support the required capabilities (by default inmemory), and the first provider that supports the capabilities is used to create the database.

Note that the Database interface is not intended to always create a database. It will do so for DerbyDatabase (inmemory), but with ExternalDatabase it simply connects to an external database based on system properties or environment variables.

JPA tester overview


database

JPATester is responsible for:

  • Starting or connecting to a database, using DatabaseBuilder.
  • Registering the acquired datasource in JNDI by first stubbing JNDI using org.wamblee.support.jndi.StubInitialContextFactory.
  • Creating the JPABuilder that will do the JPA heavy lifting.
  • Creating the DatabaseUtils for JDBC and DBUnit testing.

Copyright © 2022. All rights reserved.