Writing Custom DbUnit Data Types: The Basics

After we have finished this lesson, we:

  • Know how we can implement a custom DbUnit data type.
  • Understand how we can ensure that DbUnit uses our custom data type.

Get the source code from Github

Implementing a Custom DbUnit Data Type

When we want to implement a custom DbUnit data type, we have to create a new data type class and extend the AbstractDataType class. After we have created this class, its source code looks as follows:

1import org.dbunit.dataset.datatype.AbstractDataType;
2
3public class CustomDbUnitDataType extends AbstractDataType {
4
5}

We can now implement our custom DbUnit data type by following these steps:

First, we have to implement a constructor which invokes the constructor of the AbstractDataType class. When we invoke the constructor of the AbstractDataType class, we have to provide four constructor arguments:

  • The name of the SQL data type which is supported by the our DbUnit data type.
  • The code of the SQL data type which is supported by the our DbUnit data type.
  • The type of the object which contains the value which is inserted into a column of a database table.
  • A boolean value which specifies if our DbUnit data type supports number values.

After we have implemented the required constructor, the source code of our data type class looks as follows:

 1import org.dbunit.dataset.datatype.AbstractDataType;
 2
 3public class CustomDbUnitDataType extends AbstractDataType {
 4
 5    public CustomDbUnitDataType(String name,
 6                                int sqlType,
 7                                Class classType,
 8                                boolean isNumber) {
 9        super(name, sqlType, classType, isNumber);
10    }
11}

Second, we have to implement the typeCast() method of the DataType class. This method transforms the object given as a method parameter into an object that’s inserted into a column of a database table.

After we have implemented this method, the source code of our data type class looks as follows:

 1import org.dbunit.dataset.datatype.AbstractDataType;
 2import org.dbunit.dataset.datatype.TypeCastException;
 3
 4public class CustomDbUnitDataType extends AbstractDataType {
 5
 6    public CustomDbUnitDataType(String name,
 7                                int sqlType,
 8                                Class classType,
 9                                boolean isNumber) {
10        super(name, sqlType, classType, isNumber);
11    }
12
13    @Override
14    public Object typeCast(Object value) throws TypeCastException {
15        return null;
16    }
17}
This data type is useless because it doesn’t do anything. If you want to know how you can implement a custom DbUnit data type, you should take a look at the next lessons of this topic.

Next, we will find out how we can ensure that DbUnit uses our custom data type.

Changing the DbUnit Configuration

When we want to ensure that DbUnit uses our custom data type, we have to follow these steps:

First, we have to extend the data type factory class which ensures that DbUnit can handle the data types of the used database. Because our example uses the PostgreSQL database, we must the extend the PostgresqlDataTypeFactory class. After we have created our data type factory class, we have to override the createDataType() method of the IDataTypeFactory interface and implement it by following these steps:

  1. If the sqlType is equal to the sqlType supported by our custom DbUnit data type, return a new CustomDbUnitDataType object.
  2. Invoke the createDataType() method of the super class. This ensures that DbUnit can handle the “default” data types of the used database.

The source code of our data type factory class looks as follows:

 1import org.dbunit.dataset.datatype.DataType;
 2import org.dbunit.dataset.datatype.DataTypeException;
 3import org.dbunit.ext.postgresql.PostgresqlDataTypeFactory;
 4
 5public class CustomPostgreSQLDataTypeFactory extends PostgresqlDataTypeFactory {
 6
 7    @Override
 8    public DataType createDataType(int sqlType,
 9                                   String sqlTypeName,
10                                   String tableName,
11                                   String columnName) throws DataTypeException {
12        //Configure custom data type here
13
14        return super.createDataType(sqlType,
15                sqlTypeName,
16                tableName,
17                columnName
18        );
19    }
20}

Second, we have to create a new configuration class and annotate this class with the @Configuration annotation. After we have created this class, we have to implement it by following these steps:

  1. Create a DatabaseConfigBean bean which ensures that DbUnit uses our custom data type factory.
  2. Ensure that DbUnit uses the custom configuration provided by the DatabaseConfigBean bean.

After we have written our configuration class, its source code looks as follows:

 1import com.github.springtestdbunit.bean.DatabaseConfigBean;
 2import com.github.springtestdbunit.bean.DatabaseDataSourceConnectionFactoryBean;
 3import org.springframework.context.annotation.Bean;
 4import org.springframework.context.annotation.Configuration;
 5
 6import javax.sql.DataSource;
 7
 8@Configuration
 9public class TestDatabaseConfiguration {
10
11    @Bean
12    DatabaseConfigBean dbUnitDatabaseConfig() {
13        DatabaseConfigBean config = new DatabaseConfigBean();
14        config.setDatatypeFactory(new CustomPostgreSQLDataTypeFactory());
15        return config;
16    }
17
18    @Bean
19    DatabaseDataSourceConnectionFactoryBean dbUnitDatabaseConnection(
20            DataSource dataSource,
21            DatabaseConfigBean dbConfig
22    ) {
23        DatabaseDataSourceConnectionFactoryBean cf =
24                new DatabaseDataSourceConnectionFactoryBean();
25        cf.setDataSource(dataSource);
26        cf.setDatabaseConfig(dbConfig);
27        return cf;
28    }
29}

Before we can use our custom DbUnit data type in our DbUnit data sets, we have to ensure that our integration tests load our custom configuration class. Let’s find out how we can do it.

Configuring Our Integration Tests

When we use the @SpringBootTest annotation, we have to ensure that our integration tests load the application context of our Spring Boot application and add our custom Spring beans to the loaded application context.

After we have done this, the source code of the annotation which configures our integration tests looks as follows:

 1import java.lang.annotation.*;
 2
 3import com.github.springtestdbunit.DbUnitTestExecutionListener;
 4import com.testwithspring.master.dbunitdatatypes.CustomDbUnitDataTypeApplication;
 5import com.testwithspring.master.dbunitdatatypes.IntegrationTest;
 6import org.springframework.boot.test.context.SpringBootTest;
 7import org.springframework.test.context.TestExecutionListeners;
 8
 9@Target(ElementType.TYPE)
10@Retention(RetentionPolicy.RUNTIME)
11@Documented
12@SpringBootTest(classes = {
13        CustomDbUnitDataTypeApplication.class,
14        TestDatabaseConfiguration.class
15})
16@TestExecutionListeners(
17        listeners = {DbUnitTestExecutionListener.class},
18        mergeMode = TestExecutionListeners.MergeMode.MERGE_WITH_DEFAULTS
19)
20@IntegrationTest
21public @interface DatabaseIntegrationTest {
22}

We can now implement a custom DbUnit data type and ensure that DbUnit uses our custom data type. Let’s summarize what we learned from this lesson.

Summary

This lesson has taught us four things:

  • When we want to implement a custom DbUnit data type, we have to extend the AbstractDataType class.
  • A database specific data type factory ensures that DbUnit can handle the data types of the used database.
  • If we want to add support for new data types, we have to extend the correct data type factory class.
  • When we want to use our custom data type in our DbUnit data sets, we must ensure that that DbUnit uses our data type factory.

Get the source code from Github