Help

Built with Seam

You can find the full source code for this website in the Seam package in the directory /examples/wiki. It is licensed under the LGPL.

(Note: The following code examples only work with Seam 2.2.1 which was not released at the time of writing. Use a nightly build or a subversion checkout.)

The DBUnitSeamTest class is used for importing mock data for automated unit testing or even while deploying your application on a development machine

Adding support for PostgreSQL (or other DBMSs)

When you import mock data, constraint checking of the database management system can get in the way. For example, if you have two tables that both have a foreign key constrained field that is NULL-able, and they both reference each other. Your application might be able to INSERT and then later on UPDATE the references properly, however when writing mock data you usually would like to only execute INSERT. (Just another example why NULL is evil, by the way.)

So the DBUnitSeamTest class knows how to disable constraint checking before importing mock data, and it will re-enable constraint checking after the data has been imported. This is unfortunately vendor specific and the current out-of-the-box implementation only supports HSQL DB and MySQL. You typically set the database name you are using in your TestNG XML configuration file:

<suite name="MyTestSuite">

    <parameter name="database" value="HSQL"/>
    ...

Internally the database type is however represented with a type-safe enumeration, which makes it easy to enable/disable certain features in an actual test method or importing datasets depending on DBMS:

public class ProductTest extends DBUnitSeamTest {

    protected void prepareDBUnitOperations() {
        beforeTestOperations.add(
            new DataSetOperation("com/mydomain/skeleton/test/ProductData.dbunit.xml")
        );
        if (database.equals(Database.HSQL)) { // Note that we access the 'database' field!
            beforeTestOperations.add(
                new DataSetOperation("com/mydomain/skeleton/test/MySQLData.dbunit.xml")
            );
        }
    }

    @Test
    public void someTestMethod() throws Exception {

        new ComponentTest(){
            protected void testComponents() throws Exception {

                // This is how you hardcode a test for a particular database
                if (database.equals(Database.MYSQL)) {
                    System.out.println("Doing something special with MYSQL in this test (or not)!");
                }

            }
        }.run();
   }
}

This means that the initial list of supported DBMS is hardcoded as the enumeration DBUnitSeamTest.Database. If you want to extend this list and support other DBMS, you have to override the database field in a subclass. The following example extends the DBUnitSeamTest class with support for PostgreSQL:

public abstract class EnhancedDBUnitSeamTest extends DBUnitSeamTest {

    // These overrides enable the POSTGRESQL configuration option

    public enum Database {
        HSQL, MYSQL, POSTGRESQL
    }

    // This overrides the superclass field!
    protected Database database;

    @BeforeClass
    @Parameters("database")
    @Override
    public void setDatabase(@Optional String database) {
        this.database = Database.valueOf(database);
        // Let the superclass do its thing for databases it understands!
        switch (this.database) {
            case HSQL:
            case MYSQL:
                super.setDatabase(database);
        }
    }

    // These overrides do the actual work for POSTGRESQL support, other DBMS might require overriding
    // the disableReferentialIntegrity() and enableReferentialIntegrity() methods if they support global
    // switches.

    @Override
    protected void prepareExecution(IDatabaseConnection con, DataSetOperation operation) {
        // PostgreSQL has no global switch to turn off foreign key checks, needs to be toggled on each table
        if (database.equals(Database.POSTGRESQL)) {
            try {
                for (String tableName : operation.getDataSet().getTableNames()) {
                    con.getConnection().prepareStatement("alter table " + tableName + " disable trigger all").execute();
                }
            } catch (Exception ex) {
                throw new RuntimeException(ex);
            }
        } else {
            super.prepareExecution(con, operation);
        }
    }

    @Override
    protected void afterExecution(IDatabaseConnection con, DataSetOperation operation) {
        // PostgreSQL has no global switch to turn on foreign key checks, needs to be toggled on each table
        if (database.equals(Database.POSTGRESQL)) {
            try {
                for (String tableName : operation.getDataSet().getTableNames()) {
                    con.getConnection().prepareStatement("alter table " + tableName + " enable trigger all").execute();
                }
            } catch (Exception ex) {
                throw new RuntimeException(ex);
            }
        } else {
            super.afterExecution(con, operation);
        }
    }

}

Unfortunately PostgreSQL does not support a global turn constraint checking on/off switch but each table has to be altered before the data insertion, and again after the data has been inserted. So instead of overriding the global one-time switch methods disableReferentialIntegrity() and enableReferentialIntegrity() we override the per-operation methods in which we can get the names of the relevant tables.

Executing custom data operations

DBUnit supports standard INSERT, CLEAN, DELETE, etc. operations using datasets. The following CustomDataSetOperation is an extension example that shows how you can hook into the mock data import mechanism and execute your own import routine before or after a test method runs.

This can be used, for example, to index the imported data with Hibernate Search in Lucene, or to execute extra SQL or Stored Procedures:

public abstract class EnhancedDBUnitSeamTest extends DBUnitSeamTest {

    ... 

    protected static class CustomDataSetOperation extends DataSetOperation {

        private String output;

        public CustomDataSetOperation(String output) {
            this.output = output;
        }

        @Override
        public void execute(IDatabaseConnection connection) {
            System.out.println("OUTPUT IS: " + output);
        }
    }

}

This code only prints the 'output' message it has been given:

public class ProductTest extends EnhancedDBUnitSeamTest {

    protected void prepareDBUnitOperations() {
        beforeTestOperations.add(
            new DataSetOperation("com/mydomain/skeleton/test/ProductData.dbunit.xml")
        );
        beforeTestOperations.add(
            new CustomDataSetOperation("Hello World!")
        );
    }

    @Test
    public void someTestMethod() throws Exception {
    ...

Configuring DBUnit

If you want to set custom DBUnit features you can override the editConfig() method:

public class EnhancedDBUnitSeamTest extends DBUnitSeamTest {

    @Override
    protected void editConfig(DatabaseConfig config) {
	super.editConfig(config);

        // Enable multi-schema support
	config.setFeature("http://www.dbunit.org/features/qualifiedTableNames", true);
    }
    ...