Overriding DbUnit Configuration

After we have finished this lesson, we:

  • Know how we can create a custom data set loader that allows us to use so called replacement data sets.
  • Understand how we can change the default DbUnit configuration.

Introduction to the @DbUnitConfiguration Annotation

The @DbUnitConfiguration annotation allows us to customize the configuration of DbUnitTestExecutionListener. This annotation has four attributes that are described in the following:

  • The databaseConnection attribute contains the names of the Spring beans which provide database connections for DbUnit.
  • The dataSetLoader attribute contains the data set loader class that is used to load our DbUnit data sets.
  • The dataSetLoaderBean attribute contains the name of the data set loader bean that is used to load our DbUnit data sets.
  • The databaseOperationLookup attribute contains the class that is used to lookup DbUnit database operations.

Let’s move on and find out how we can create a custom data set loader, which allows us to use so called replacement data sets, and use this data set loader for loading our DbUnit data sets.

Using Replacement Data Sets

The first thing that we have to do is to create a custom data set loader class. We can create our data set loader class by following these steps:

First, we have to create a class that extends the FlatXmlDataSetLoader class. We have to extend that class because we want that our data set loader supports flat XML data sets and enables column sensing.

After we have created our data set loader class, its source code looks as follows:

1import com.github.springtestdbunit.dataset.FlatXmlDataSetLoader;
2
3public class ReplacementDataSetLoader extends FlatXmlDataSetLoader {
4
5}

Second, we have to create a private method that transforms an IDataSet object into a ReplacementDataSet object and returns the created object. This method configures the placeholder strings and the actual values that are used to replace these placeholder strings.

We can implement this method by following these steps:

  1. Add a private createReplacementDataSet() method into our data set loader class. This method takes an IDataSet object as a method parameter and returns an IDataSet object.
  2. Create a new ReplacementDataSet object.
  3. Ensure that the [empty_string] strings found from our data set are replaced with an empty string.
  4. Ensure that the [null] strings found from our data set are replaced with null values.
  5. Return the created ReplacementDataSet object.

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

 1import com.github.springtestdbunit.dataset.FlatXmlDataSetLoader;
 2import org.dbunit.dataset.IDataSet;
 3import org.dbunit.dataset.ReplacementDataSet;
 4
 5public class ReplacementDataSetLoader extends FlatXmlDataSetLoader {
 6
 7    private IDataSet createReplacementDataSet(IDataSet flatXmlDataSet) {
 8        ReplacementDataSet replacementDataSet =
 9				new ReplacementDataSet(flatXmlDataSet);
10
11        replacementDataSet.addReplacementObject("[empty_string]", "");
12        replacementDataSet.addReplacementObject("[null]", null);
13
14        return replacementDataSet;
15    }
16}

Third, we have override the createDataSet() method. When we implement this method, we have to obtain the loaded flat XML data set by invoking the createDataSet() method of the super class and transform this data set into a replacement data set.

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

 1import com.github.springtestdbunit.dataset.FlatXmlDataSetLoader;
 2import org.dbunit.dataset.IDataSet;
 3import org.dbunit.dataset.ReplacementDataSet;
 4import org.springframework.core.io.Resource;
 5
 6public class ReplacementDataSetLoader extends FlatXmlDataSetLoader {
 7
 8    @Override
 9    protected IDataSet createDataSet(Resource resource) throws Exception {
10        IDataSet flatXmlDataSet = super.createDataSet(resource);
11        return createReplacementDataSet(flatXmlDataSet);
12    }
13
14    private IDataSet createReplacementDataSet(IDataSet flatXmlDataSet) {
15        ReplacementDataSet replacementDataSet =
16				new ReplacementDataSet(flatXmlDataSet);
17
18        replacementDataSet.addReplacementObject("[empty_string]", "");
19        replacementDataSet.addReplacementObject("[null]", null);
20
21        return replacementDataSet;
22    }
23}

We have now created our custom data set loader class. Our next step is to ensure that the DbUnitTestExecutionListener uses our custom data set loader class.

If we want to use a custom data set loader, we have to configure the used data set loader class by annotating our integration test class with the @DbUnitConfiguration annotation.

After we have configured our custom data set loader, the source code of our test class looks as follows:

 1import com.github.springtestdbunit.DbUnitTestExecutionListener;
 2import com.github.springtestdbunit.annotation.DatabaseSetup;
 3import com.github.springtestdbunit.annotation.DbUnitConfiguration;
 4import org.junit.Test;
 5import org.junit.experimental.categories.Category;
 6import org.junit.runner.RunWith;
 7import org.springframework.beans.factory.annotation.Autowired;
 8import org.springframework.test.context.ContextConfiguration;
 9import org.springframework.test.context.TestExecutionListeners;
10import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
11import org.springframework.test.context.support.DependencyInjectionTestExecutionListener;
12import org.springframework.test.context.transaction.TransactionalTestExecutionListener;
13
14import java.util.Optional;
15
16import static org.assertj.core.api.Assertions.assertThat;
17
18@RunWith(SpringJUnit4ClassRunner.class)
19@ContextConfiguration(classes = {PersistenceContext.class})
20@TestExecutionListeners({
21        DependencyInjectionTestExecutionListener.class,
22        TransactionalTestExecutionListener.class,
23        DbUnitTestExecutionListener.class
24})
25@DatabaseSetup("messages.xml")
26@DbUnitConfiguration(dataSetLoader = ReplacementDataSetLoader.class)
27@Category(IntegrationTest.class)
28public class FindMessageByIdTest {
29
30    @Autowired
31    private MessageRepository repository;
32
33    @Test
34    public void shouldReturnOptionalThatContainsMessageWhenMessageIsFound() {
35        Optional<Message> found = repository.findById(Messages.HelloWorld.ID);
36        assertThat(found).isNotEmpty();
37    }
38}

When we run our integration test and use a data set that tries to insert an empty string to our database, our integration test fails and we see the following error message:

1java.lang.IllegalArgumentException: table.column=messages.MESSAGE_TEXT value
2is empty but must contain a value (to disable this feature check, set
3DatabaseConfig.FEATURE_ALLOW_EMPTY_FIELDS to true)

The source code of our DbUnit data set looks as follows:

1<dataset>
2    <messages id="1"
3              message_text="Hello World!"/>
4    <messages id="2"
5              message_text="[empty_string]"/>
6</dataset>

Let’s fix this problem by overriding the default configuration of DbUnit.

Modifying the Default Configuration of DbUnit

When we want change the default configuration of DbUnit, we have to create a new application context configuration class by following these steps:

First, we have to create an application context configuration class called IntegrationTestContext and ensure that this class imports the application context configuration class of our application.

After we have created the IntegrationTestContext class, its source code looks as follows:

1import org.springframework.context.annotation.Configuration;
2import org.springframework.context.annotation.Import;
3
4@Configuration
5@Import({PersistenceContext.class})
6public class IntegrationTestContext {
7
8}

Second, we have to implement a method that configures the DatabaseConfigBean bean which allows us to modify the used DbUnit configuration. Because we want to ensure that DbUnit allows us to use empty strings in INSERT statements, we have to set the value of the allowEmptyFields configuration option to true.

After we have configured the DatabaseConfigBean bean, the source code of the IntegrationTestContext class looks as follows:

 1import org.springframework.context.annotation.Bean;
 2import org.springframework.context.annotation.Configuration;
 3import org.springframework.context.annotation.Import;
 4
 5@Configuration
 6@Import({PersistenceContext.class})
 7public class IntegrationTestContext {
 8
 9    @Bean
10    DatabaseConfigBean databaseConfigBean() {
11        DatabaseConfigBean config = new DatabaseConfigBean();
12        config.setAllowEmptyFields(true);
13        return config;
14    }
15}

Third, We have to implement a method that configures the DatabaseDataSourceConnectionFactoryBean which provides database connections for DbUnit. This method must be called: dbUnitDatabaseConnection(), and it takes the DataSource and DatabaseConfigBean beans as method parameters.

We can implement this method by creating a new DatabaseDataSourceConnectionFactoryBean object and returning the created object.

After we have configured the DatabaseDataSourceConnectionFactoryBean bean, the source code of the IntegrationTestContext class 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;
 5import org.springframework.context.annotation.Import;
 6
 7import javax.sql.DataSource;
 8
 9@Configuration
10@Import({PersistenceContext.class})
11public class IntegrationTestContext {
12
13    @Bean
14    DatabaseConfigBean databaseConfigBean() {
15        DatabaseConfigBean config = new DatabaseConfigBean();
16        config.setAllowEmptyFields(true);
17        return config;
18    }
19
20    @Bean
21    DatabaseDataSourceConnectionFactoryBean dbUnitDatabaseConnection(DataSource dataSource,
22                                                                     DatabaseConfigBean databaseConfigBean) {
23        DatabaseDataSourceConnectionFactoryBean cf = new DatabaseDataSourceConnectionFactoryBean();
24        cf.setDataSource(dataSource);
25        cf.setDatabaseConfig(databaseConfigBean);
26        return cf;
27    }
28}
If you are using XML configuration, check the example application of this lesson . It has a working XML configuration file .

We have now created the required Spring beans. Next we have to ensure that our integration test class uses our new configuration.

Because the name of the DatabaseDataSourceConnectionFactoryBean is dbUnitDatabaseConnection, we don’t have to make any changes to the attributes of the @DbUnitConfiguration annotation. This means that we have to only change the application context configuration class that configures the application context of our integration tests.

After we have made the required change, the source code of our integration test class looks as follows:

 1import com.github.springtestdbunit.DbUnitTestExecutionListener;
 2import com.github.springtestdbunit.annotation.DatabaseSetup;
 3import com.github.springtestdbunit.annotation.DbUnitConfiguration;
 4import org.junit.Test;
 5import org.junit.experimental.categories.Category;
 6import org.junit.runner.RunWith;
 7import org.springframework.beans.factory.annotation.Autowired;
 8import org.springframework.test.context.ContextConfiguration;
 9import org.springframework.test.context.TestExecutionListeners;
10import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
11import org.springframework.test.context.support.DependencyInjectionTestExecutionListener;
12import org.springframework.test.context.transaction.TransactionalTestExecutionListener;
13
14import java.util.Optional;
15
16import static org.assertj.core.api.Assertions.assertThat;
17
18@RunWith(SpringJUnit4ClassRunner.class)
19@ContextConfiguration(classes = {IntegrationTestContext.class})
20@TestExecutionListeners({
21        DependencyInjectionTestExecutionListener.class,
22        TransactionalTestExecutionListener.class,
23        DbUnitTestExecutionListener.class
24})
25@DatabaseSetup("messages.xml")
26@DbUnitConfiguration(dataSetLoader = ReplacementDataSetLoader.class)
27@Category(IntegrationTest.class)
28public class FindMessageByIdTest {
29
30    @Autowired
31    private MessageRepository repository;
32
33    @Test
34    public void shouldReturnOptionalThatContainsFoundMessageWhenMessageIsFound() {
35        Optional<Message> found = repository.findById(Messages.HelloWorld.ID);
36        assertThat(found).isNotEmpty();
37    }
38}

If you are using Spring Boot, you can configure the used application context configuration class by using the @SpringBootTest annotation:

1@SpringBootTest(classes = {IntegrationTestContext.class})

When we run our integration tests, we notice that they pass.

Let’s summarize what we learned from this lesson.

Summary

This lesson has taught us three things:

  • The @DbUnitConfiguration annotation allows us to modify the configuration of DbUnitTestExecutionListener.
  • If we want to support replacement data sets, we have to create a new data set loader that transforms our original data sets into ReplacementDataSet objects.
  • If we want to modify the used DbUnit configuration, we have to create a new DatabaseConfigBean bean.

Get the source code from Github