Tuesday 8 March 2011

Testing JPA Entities using DBUnit

DBUnit is a powerful testing tool that allows you to put your database into a known state in between tests. A common problem (and one of poor test design) is where a test changes the state of an object and this state affects the testing of objects in later tests. Each test should be independent and tests within a suite should be able to run in any order. This post will aim to describe the code needed to setup a database before each run of a JUnit test method.

The below class has three key methods:
  • initEntityManager() - this method is annotated with @BeforeClass so will only be called once for this test and called before any tests are run. The method will create an entity manager, get a connection to an instance of the in memory Java database HSQL and then populate it by reading in the flat xml database record structure that is contained in the file test-dataset.xml. For alternative dataset formats please visit http://www.dbunit.org/apidocs/org/dbunit/dataset/IDataSet.html
  • closeEntityManager() - this method is annotated with @AfterClass so will only be called once for this test and called after all the tests have been run. The method will close down the entity manager and the entity manager connection factory.
  • cleanDB() - this method is annotated with @Before and will be called before every test method call. The call to  DatabaseOperation.CLEAN_INSERT.execute will clean the database tables and insert the records from the dataset xml file. Database operations other than CLEAN_INSERT are described below:
            UPDATE        updates the database using the data in the dataset.
            INSERT         inserts the data in the dataset into the database.
            DELETE         deletes only the data in the dataset from the database.
            DELETE_ALL deletes all rows of tables present in the specified dataset.
            TRUNCATE   truncate tables listed in the dataset.
            REFRESH     refreshes the database using the data in the dataset.
            NONE           does what it says, nothing.

import org.dbunit.database.DatabaseConfig;
import org.dbunit.database.DatabaseConnection;
import org.dbunit.database.IDatabaseConnection;
import org.dbunit.dataset.IDataSet;
import org.dbunit.dataset.xml.FlatXmlDataSetBuilder;
import org.dbunit.ext.hsqldb.HsqldbDataTypeFactory;
import org.dbunit.operation.DatabaseOperation;
import org.hibernate.impl.SessionImpl;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
import org.junit.Test;

public class JPATest {

    protected static EntityManagerFactory entityManagerFactory;
    protected static EntityManager entityManager;
    protected static IDatabaseConnection connection;
    protected static IDataSet dataset;

    @BeforeClass
    public static void initEntityManager() throws Exception {
        entityManagerFactory = Persistence.createEntityManagerFactory("PersistenceUnit");
        entityManager = entityManagerFactory.createEntityManager();
        connection = new DatabaseConnection(((SessionImpl)(entityManager.getDelegate())).connection());
        connection.getConfig().setProperty(DatabaseConfig.PROPERTY_DATATYPE_FACTORY, new HsqldbDataTypeFactory());

        FlatXmlDataSetBuilder flatXmlDataSetBuilder = new FlatXmlDataSetBuilder();
        flatXmlDataSetBuilder.setColumnSensing(true);
        dataset = flatXmlDataSetBuilder.build(
        Thread.currentThread().getContextClassLoader().getResourceAsStream("test-dataset.xml"));
    }

    @AfterClass
    public static void closeEntityManager() {
        entityManager.close();
        entityManagerFactory.close();
    }

    @Before
    public void cleanDB() throws Exception {
        DatabaseOperation.CLEAN_INSERT.execute(connection, dataset);
    }

    @Test
    public void testAUsefulMethod() throws Exception {
        // .... test code
    }

}


An example structure of the dataset.xml is shown below:

<?xml version='1.0' encoding='UTF-8'?>
<dataset>
    <customer id="1" surname="SURNAME" firstName="FIRSTNAME" middleName="MIDDLENAME" personalAccount="true"/>
    <customer id="2" surname="SURNAME" firstName="FIRSTNAME" personalAccount="true" balance="100000.00/>
</dataset>


One point of note is that when deleting database records using DBUnit the same rules apply as if you were using SQL. You will not be able to delete a record if its primary key is a foreign key on another table. An example in Flat XML for a Customer class that has many Addresses would be:

<?xml version='1.0' encoding='UTF-8'?>
<dataset>
    <customer/>
    <address/>
    <customer_address/>
</dataset>


Finally, there are many useful methods on IDataSet and ITable interfaces. An example being to obtain the number of records of a particular table:

int customerRecordCount = dataset.getTable("Customer").getRowCount();

Monday 7 March 2011

Useful JMS-related Glassfish and OpenMq commands

Below are a few Glassfish JMS admin commands and some useful OpenMQ commands (to query, purge and list) which can be be used in scripts to perform tasks as opposed to performing the same procedures via the Glassfish or OpenMQ admin consoles:


Glassfish JMS Commands

To clear previous JMS setup

asadmin delete-jms-resource <JMS_TOPIC_RESOURCE_NAME>
asadmin delete-jms-resource <JMS_TOPIC_CONNECTION_FACTORY_NAME>


To create the JMS Topic Connection factory

asadmin create-jms-resource --restype=javax.jms.TopicConnectionFactory --property transaction-support=LocalTransaction --description="JMS Topic Connection Factory." <JMS_TOPIC_CONNECTION_FACTORY_NAME>


To create the JMS Topic resource

asadmin create-jms-resource --restype=javax.jms.Topic --description="JMS Topic" <JMS_TOPIC_RESOURCE_NAME>


To list JMS resources

asadmin list-jms-resources


OpenMQ Commands

Query the JMS resources

imqcmd query bkr  -b <host>:<port> -passfile <password file> -u <user name>

Purge the Topic (or Queue)

imqcmd purge dst -f -passfile <password file> -n <topic name> -t t -b <host>:<port> -u <user name>

List the message rate and packet flow

imqcmd list dst -passfile <password file> -b <host>:<port> -u <user name>

An example output from the list command would be:

Listing all the destinations on the broker specified by:

-------------------------
Host         Primary Port
-------------------------
localhost    7676

-----------------------------------------------------------------------------------------------------------------
             Name                Type    State      Producers        Consumers                  Msgs             
                                                 Total  Wildcard  Total  Wildcard  Count  Remote  UnAck  Avg Size
-----------------------------------------------------------------------------------------------------------------
JMSTopic                         Topic  RUNNING  0      0         1      0         0      0       0      0.0
mq.sys.dmq                       Queue  RUNNING  0      -         0      -         171    0       0      5273.322