Test Conventions

Overview

This page covers writing dbUnit’s own test suite as a contributor. Looking for how to use IDatabaseTester/DBTestCase in your own project’s tests? See Test Integration instead — that page is about testing with dbUnit; this one is about testing dbUnit itself.

Test Types & Suffixes

  • <ClassName>Test — a unit test. No database required; runs in the normal Maven test phase via Surefire.
  • <ClassName>IT — an integration test. Any test that requires a database to work is an integration test, even if the logic under test looks simple. Runs in the Maven integration-test phase via Failsafe. See Integration Tests for how to run these against a specific database.

Test Method Naming

test<MethodName>_<StartingStateConditions>_<AssertedOutcome>. For example, from TableFormatterTest:

void testFormatSimpleTable_withTwoRows_returnsFormattedTableString() throws Exception

The method under test is formatSimpleTable (conceptually — TableFormatter.format), the starting condition is "with two rows," and the asserted outcome is "returns a formatted table string."

Coverage Expectation

Every change needs unit test coverage added or updated to cover it — proving a bug fix actually fixes the reported problem, and proving a new feature works. Tests also carry long-term value beyond the immediate change: they prove future changes don’t break the behavior they cover.

Assertions

Prefer AssertJ. Prefer asserting the actual object against an expected object as a whole, rather than field-by-field or value-by-value. The same TableFormatterTest example above does this correctly — it builds one expected string and asserts the whole thing in a single isEqualTo, rather than checking fragments of the output individually:

final String expected =
        "****** table: MY_TABLE ** row count: 2 ******\n"
                + "COL1                |COL2                |\n"
                + "====================|====================|\n"
                + "my string value     |39284.1             |\n"
                + "my string value2    |2                   |\n";
assertThat(actual).isEqualTo(expected);

Use an .as() for the test fail message on an assertion and end the message with a period so it doesn’t combine with the subsequent JUnit message, e.g. assertThat(actual.getMessage()).as("Should have null message.").isNull();.

Test Doubles: Composition Over Inheritance

When a test needs to spy on or intercept calls to a real collaborator — most often an IMetadataHandler, IDataTypeFactory, or similar dbUnit extension-point interface — wrap it, don’t subclass a concrete implementation. A test double that extends a specific vendor class (e.g. H2MetadataHandler) only works while the connection under test uses that one vendor, and Java’s single inheritance means it cannot also extend a second vendor’s class if that ever changes. A test double that implements the interface and holds a delegate field, forwarding every method to it except the one or two being spied on, stays correct regardless of which concrete implementation the connection under test actually uses:

private static class TestMetadataHandler implements IMetadataHandler
{
    private final IMetadataHandler delegate;
    private final Set<String> schemaSet = new HashSet<>();

    TestMetadataHandler(final IMetadataHandler delegate)
    {
        this.delegate = delegate;
    }

    @Override
    public ResultSet getTables(final DatabaseMetaData metaData,
            final String schemaName, final String[] tableType) throws SQLException
    {
        schemaSet.add(schemaName);
        return delegate.getTables(metaData, schemaName, tableType);
    }

    // ...every other IMetadataHandler method delegates to `delegate` unchanged.
}

See DatabaseDataSet_MultiSchemaTest for the full example: it wraps whichever IMetadataHandler the connection under test is actually configured with (H2MetadataHandler, in that test’s case) instead of extending it, so the test stays correct even if H2 support’s implementation changes, and would work unmodified if the test ever moved to a different vendor.

Fixed Test Timezone

Surefire and Failsafe both force -Duser.timezone=Europe/Berlin on every test run (confirmed in `pom.xml’s plugin configuration) — so timestamp/timezone-sensitive behavior is exercised consistently in CI regardless of the runner’s own locale. A test launched directly from an IDE skips this unless you configure the run configuration to match. See Building's Quality Gates section for the mechanics.

Java Style

This page covers test-specific conventions only. For general Java style, see Java Style & Tooling.