Integration test support

Take advantage of an out-of-the-box integration test support to write your own custom integration tests for the database of your choice using the standard integration test infrastructure.

External resources

Overview

  • Project: daofusion-test
  • Package reference: com.anasoft.os.daofusion.test.*

Introduction

Having a generic DAO pattern implementation with all of its handy features is nice, but how can one be sure that it works properly for a specific database? This is actually a quite important aspect since developers using DAO Fusion need to feel "safe" when using the out-of-the-box DAO functionality. The basic idea behind the integration test support is to encourage developers to use DAO Fusion, given that:

  • all standard DAO implementations are properly and thoroughly tested using a non-trivial sample domain model for all major databases (standard core integration tests)
  • it is possible to write custom integration tests for the chosen database using the standard integration test infrastructure (custom generic / core integration tests)

An integration test is basically a unit test interacting with the database instance via JPA / Hibernate, employing a default method-level rollback strategy which ensures proper test data separation between multiple test runs. DAO Fusion achieves this using JUnit and Spring TestContext framework in a consistent integration test infrastructure.

There are basically two kinds of integration tests:

  • generic integration tests - aimed at testing custom business-related DAO functionality
  • core integration tests - aimed at testing core DAO functionality provided by standard DAO implementations using a non-trivial sample domain model

Standard database support

DAO Fusion is currently integration-tested against following databases:

  • In-memory databases

This means that DAO Fusion includes standard integration tests for databases listed above as part of its unit tests within the daofusion-test project. You are free to check out the daofusion-test project from the source code repository and run these tests by yourself. This is, however, not quite necessary since our continuous integration system performs this task when building DAO Fusion automatically.

To sum it up, standard core integration tests need not to be run manually - the daofusion-test project is intended for use as a test-scoped library, providing the integration test infrastructure for custom integration tests.

Integration test infrastructure overview

BaseHibernateIntegrationTest represents a generic integration test which is agnostic of the underlying database instance in use. BaseHibernateIntegrationTest uses the SpringJUnit4ClassRunner which provides the functionality of Spring TestContext framework to standard JUnit tests. BaseHibernateIntegrationTest declares two test execution listeners vital for integration tests (DependencyInjectionTestExecutionListener and TransactionalTestExecutionListener) along with the default method-level rollback strategy via the TransactionConfiguration annotation.

BaseHibernateIntegrationTest defines its own parent Spring context for all integration tests. This context contains everything necessary to set up the basic JPA / Hibernate test environment, including the following components:

  • pooled data source providing database access via JDBC
  • JPA entity manager factory with Hibernate JPA vendor adapter
  • JPA transaction manager for a single thread-bound JPA entity manager factory
  • AOP transaction support

In order to make specific integration tests work properly, some additional configuration steps need to be performed:

  • Following Spring bean properties need to be set up via the PropertyOverrideConfigurer according to the database instance in use:
    • dataSource.driverClass
    • dataSource.jdbcUrl
    • dataSource.user
    • dataSource.password
    • entityManagerFactory.persistenceUnitName
  • Within the META-INF/persistence.xml file a dedicated integration test persistence unit of type RESOURCE_LOCAL needs to be defined for the target database instance. Name of this persistence unit must match the entityManagerFactory.persistenceUnitName bean property value. The hibernate.dialect property needs to be set up as well within the persistence unit.
  • Additional persistence unit configuration (e.g. defining entity mappings or setting the default schema name) should be done via the mapping-file element(s) within META-INF/persistence.xml.

BaseHibernateIntegrationTest introduces a test profile convention for running fine-grained integration tests against local / remote databases. Assume that your application includes several custom integration tests for local and remote databases: a local database installed on developer's machine and a remote database reachable from the company's continuous integration system. It's convenient for the developer to run integration tests locally before commiting his changes. On the other hand, the continuous integration system can run integration tests against the remote database dedicated for this purpose. The general rule of thumb is to have local / remote databases of the same kind as "real" production ones. See the BaseHibernateIntegrationTest Javadoc for more information about the test profile convention as well as other useful hints.

BaseHibernateCoreIntegrationTest is an extension of the BaseHibernateIntegrationTest for core integration tests aimed at standard persistent entity DAO implementations. Core integration tests operate on the sample eShop domain model defined within the example subpackage. This domain model includes persistent entity and enumeration classes as well as corresponding DAO implementations. BaseHibernateCoreIntegrationTest is actually a base class for core integration tests - all of the configuration details mentioned above still applies to its subclasses. The main purpose of BaseHibernateCoreIntegrationTest is to inject eShop DAO dependencies as well as preparing initial test data to be shared by all test cases.

There are two abstract core integration test classes derived from BaseHibernateCoreIntegrationTest which correspond to standard DAO implementations: AbstractHibernateEntityDaoTest and AbstractHibernateEnumerationDaoTest. These classes contain the actual test code working with sample DAO implementations. Since they are abstract, the user needs to extend and configure them for a specific database instance.

As for custom generic integration tests aimed at testing business-related DAO functionality, you should extend the BaseHibernateIntegrationTest directly and provide all the configuration details regarding target database instance(s). The best way to start writing such integration tests is to examine the integration test infrastructure and the way how standard core integration tests use it in detail.

DAO versus service

Assuming that you follow the general multi-tier client-server architecture within your application, there are a couple of things that set the DAO and service layers apart from each other:

  • business-related DAO functionality should always be atomic from a persistent entity point of view, for example:
    • entity lookup based on custom business criteria that comes from a specific use case scenario regarding that entity
  • everything else that performs complex business operations (involving multiple persistent entity types) should be implemented in the service layer, for example:
    • operation that validates the input, creates multiple entities (including their associations) and persists them into the database

Note that the service layer doesn't need integration tests, assuming the DAO layer itself is integration-tested. A service is, after all, just using the DAO to perform complex business operations. It's generally recommended to follow the mock object approach for unit-testing the service layer in a way that abstracts from the underlying data access and puts focus to the actual business problem.

Writing a custom core integration test

So, you have your favorite database for which you want to test the core DAO functionality using the standard integration test infrastructure. First thing you need to do is to extend AbstractHibernateEntityDaoTest / AbstractHibernateEnumerationDaoTest like this (we use PostgreSQL in this case):

@ContextConfiguration(locations = { DatabaseContextLocations.POSTGRESQL })
@IfProfileValue(name = BaseHibernateIntegrationTest.PROFILE_DBTYPE_NAME, values = {
        BaseHibernateIntegrationTest.PROFILE_DBTYPE_VALUE_ALL,
        BaseHibernateIntegrationTest.PROFILE_DBTYPE_VALUE_LOCAL })
public class PostgreSQLHibernateEntityDaoTest extends AbstractHibernateEntityDaoTest {

    // standard test methods inherited from the parent

}

DatabaseContextLocations.POSTGRESQL should point to a Spring context for a local PostgreSQL database instance:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd">

	<!-- bean property value resolution -->
	<context:property-override
		location="classpath:postgresql/test-postgresql-beanSetup.properties" />

</beans>

The test-postgresql-beanSetup.properties file referenced by this context would look like this:

# pooled JDBC data source configuration
dataSource.driverClass=org.postgresql.Driver
dataSource.jdbcUrl=jdbc:postgresql://localhost:5432/postgres
dataSource.user=postgres
dataSource.password=postgres

# name of the integration test persistence unit
entityManagerFactory.persistenceUnitName=dao-test-postgresql

Since JPA uses META-INF/persistence.xml as the default persistence metadata descriptor, you need to provide this file as well:

<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd"
	version="1.0">

	<persistence-unit name="dao-test-postgresql" transaction-type="RESOURCE_LOCAL">
		<mapping-file>test-core-orm.xml</mapping-file>
		<properties>
			<property name="hibernate.dialect" value="org.hibernate.dialect.PostgreSQLDialect" />
		</properties>
	</persistence-unit>

</persistence>

How cool is that? Using the standard integration test infrastructure that takes care of the JPA / Hibernate test environment, we are able to write custom core integration tests with only a few lines of Java code and configuration. Notice that we're using the test-core-orm.xml file defining entity mappings for the sample eShop domain model.