DbUnitExtension

Overview

DbUnitExtension is a JUnit 5/6 (Jupiter) extension that drives the IDatabaseTester setup/teardown lifecycle automatically, so a test class doesn’t need its own @BeforeEach/@AfterEach pair calling onSetup()/onTearDown().

Register it with @ExtendWith(DbUnitExtension.class), or with the shorter @DbUnitTest. The test class still holds its own IDatabaseTester field — this is the same composition style as the IDatabaseTester guide, just with the lifecycle calls automated instead of written by hand:

@ExtendWith(DbUnitExtension.class)
class AccountRepositoryTest
{
    IDatabaseTester databaseTester;

    AccountRepositoryTest() throws ClassNotFoundException
    {
        databaseTester =
                new JdbcDatabaseTester("org.h2.Driver", "jdbc:h2:mem:example;DB_CLOSE_DELAY=-1");
    }

    @BeforeEach
    void loadDataset() throws Exception
    {
        databaseTester.setDataSet(new FlatXmlDataSetBuilder().build(new File("prep.xml")));
        databaseTester.setTearDownOperation(DatabaseOperation.DELETE_ALL);
    }

    @Test
    void testWithdraw_sufficientBalance_decrementsBalance() { ... }
}

@BeforeEach methods still run first — configure the dataset and any operation overrides there. The extension then calls onSetup() immediately before the test method and onTearDown() immediately after it, even if the test method fails or onSetup() itself throws.

Field Discovery

The extension finds the IDatabaseTester by scanning the test instance’s fields, including inherited ones:

  • The nearest declaring class wins — a field on the test class itself takes precedence over one on a superclass.
  • That class must declare exactly one non-static field assignable to IDatabaseTester. No match anywhere in the hierarchy, or two-or-more matches at the same class level, both fail fast with a descriptive IllegalStateException rather than guessing.
  • Static fields are ignored.
  • Private fields are found; the field’s own access modifier doesn’t matter.

Field discovery also walks every enclosing test instance, innermost first, so a @Nested test class inherits its enclosing class’s tester field.

Annotation-Driven Configuration

Rather than a @BeforeEach method, the dataset and operation can be declared directly on the class or method with org.dbunit.annotation — @DbUnitPrep, @DbUnitSetup, @DbUnitExpected, @DbUnitTearDown, and @DbUnitConfig. This also unlocks the PrepAndExpectedTestCase prep/expected path declaratively, and a @DbUnitTester/@DbUnitTestCase field marker in place of the plain-field auto-scan described above. See DbUnit Annotations for the full reference.

Row Count Check

The same RowCountCheck diagnostic available to PrepAndExpectedTestCase applies here too: a baseline is captured from the resolved IDatabaseTester’s connection before `onSetup() and verified after onTearDown(), failing the test by name when a table’s row count moved. Verification is skipped whenever onTearDown() itself throws, and also when the test method threw before reaching it, since either way the database is left in an unknown state and a count difference would only be noise around the real failure. It is off by default; enable it with DatabaseConfig.FEATURE_ROW_COUNT_CHECK (or -Ddbunit.rowCountCheck=true) on the connection’s config, or per class/method with @DbUnitRowCountCheck.

Parameter Injection

DbUnitExtension also resolves IDatabaseTester, PrepAndExpectedTestCase, IDatabaseConnection, and Connection parameters on @Test and @BeforeEach methods, so a test can ask for the tester or its connection directly instead of reaching through a field:

@DbUnitTest
class AccountRepositoryTest
{
    IDatabaseTester databaseTester;

    AccountRepositoryTest() throws ClassNotFoundException
    {
        databaseTester =
                new JdbcDatabaseTester("org.h2.Driver", "jdbc:h2:mem:example;DB_CLOSE_DELAY=-1");
    }

    @Test
    void testWithdraw_sufficientBalance_decrementsBalance(final Connection connection)
            throws Exception
    {
        // use connection directly
    }
}

Parameter injection is claimed only for a test that opts into org.dbunit.annotation — one carrying @DbUnitTest or any @DbUnit* annotation on the class or method, or a @DbUnitTester/ @DbUnitTestCase field. A bare @ExtendWith(DbUnitExtension.class) class with only a plain, unannotated IDatabaseTester field keeps the lifecycle-only behavior it has always had and no parameter is claimed for it, so another extension resolving a Connection (or any of the other three types) on the same test method is not left competing with a claim here. Switch such a class to @DbUnitTest, or mark its field @DbUnitTester, to opt its parameters in.

On an annotation-driven test this extension claims every java.sql.Connection parameter. If another extension on the same test also resolves java.sql.Connection — Spring’s SpringExtension, Testcontainers, a JPA/JDBC harness — JUnit fails the test with "discovered multiple competing ParameterResolvers". Set @DbUnitConfig(injectConnectionParameter = false) to yield the bare Connection to that extension; the dbUnit-specific IDatabaseConnection parameter is still resolved (no other framework claims that type), so inject it and call its getConnection().

A @BeforeEach parameter resolves before this extension’s own setup runs — JUnit Jupiter always calls @BeforeEach methods first — so it sees the tester/connection exactly as they exist before that setup: @DbUnitPrep’s dataset, if any, is not loaded onto it yet. Ask for the parameter on the `@Test method instead when the test needs the already-prepped state.

Not supported on @AfterEach, @AfterAll, @BeforeAll, or a test class constructor — declined outright rather than resolved. By the time @AfterEach runs, this extension’s own afterTestExecution() callback has already closed (or never resolved) the connection it manages, so resolving one there would hand out an already-closed connection, or leak a freshly-opened one nothing would ever close. The same timing catches a direct databaseTester.getConnection() call in an @AfterEach body: on a fixed-connection tester it returns the connection this extension just closed; on a fresh-connection tester (JdbcDatabaseTester) it opens one nothing will close. Do post-test database work in the @Test method itself, or in an @AfterEach that fully owns its own connection. The injected IDatabaseConnection/Connection is managed for the test’s duration and closed afterward — by the extension on the setup/teardown path, by the driven PrepAndExpectedTestCase on the prep/expected path; do not close it yourself.

When to Use This Instead of Manual Lifecycle Calls

Reach for DbUnitExtension when a @BeforeEach/@AfterEach pair that only calls onSetup()/onTearDown() (as shown in the IDatabaseTester guide) would just be boilerplate repeated across every test class. Write the @BeforeEach/@AfterEach pair yourself instead when a test needs other logic around those calls, or targets a JUnit version this extension doesn’t support.