Class AnnotatedTestExecutor

java.lang.Object
org.dbunit.annotation.runtime.AnnotatedTestExecutor

public class AnnotatedTestExecutor extends Object
Drives one test's dbUnit lifecycle from a resolved AnnotatedTestConfiguration, dispatching beforeTest()/afterTest(boolean) to one of two path objects - SetupTeardownLifecycle when AnnotatedTestConfiguration.isExpected() is false, ExpectedLifecycle when it is true - and owning what both share: the one TestScopedConnection this test's steps reuse, its ConnectionOwnership decision, the @DbUnitProperty application, and (for an annotation-driven test) the ExecutorOperationListener installed on the tester.

Not intended for direct use by test code; this is machinery consumed by a binding such as DbUnitExtension, which resolves the IDatabaseTester and any injected PrepAndExpectedTestCase - field discovery is binding-specific - and hands them here already resolved.

The binding tells this executor, through the constructor's annotationDriven flag, whether the test opted into the org.dbunit.annotation family at all - any @DbUnit* annotation, or a @DbUnitTester/@DbUnitTestCase field. When it did not - a bare @ExtendWith(DbUnitExtension.class) class with one plain, unannotated IDatabaseTester field, the 3.5.0 lifecycle-only style - this executor takes the classic path: installOperationListener() does not run, so the tester's IOperationListener is left untouched, and AnnotatedRowCountCheck never piggybacks (it captures its baseline eagerly and SetupTeardownLifecycle lets onSetup()/onTearDown() manage their own connections) - exactly as DbUnitExtension did before this class existed. The prep/expected path is always annotation-driven (it needs @DbUnitExpected).

The connection getConnection() memoizes - the tester's own on the setup/teardown path, PrepAndExpectedTestCase.getReusableConnection()'s on the prep/expected path so a Connection/IDatabaseConnection parameter shares the test case's own - is used by the row count check and by a binding's parameter injection, and closed in afterTest(boolean) when ConnectionOwnership.mayClose() allows it. On an annotation-driven test the ExecutorOperationListener additionally shields that one connection, by identity, from a premature close by the tester's own onSetup()/ onTearDown() machinery while still forwarding a close for any other connection the tester hands out.

The row count check (see org.dbunit.database.rowcount.RowCountCheck) runs, on the setup/teardown path, through AnnotatedRowCountCheck; the prep/expected path already has its own via DefaultPrepAndExpectedTestCase.preTest() and cleanupData(). Either way, when @DbUnitRowCountCheck is declared its resolved RowCountCheck overrides whichever of the two would otherwise resolve one from the connection's own DatabaseConfig - for the prep/expected path via DefaultPrepAndExpectedTestCase.setRowCountCheckOverride(boolean, String[]) (applied by InjectedTestCaseConfigurer), which resolves that connection lazily on its own so this executor needs none just to build the override.

Since:
3.6.0
Author:
Jeff Jensen
  • Constructor Details

    • AnnotatedTestExecutor

      public AnnotatedTestExecutor(AnnotatedTestConfiguration configuration, IDatabaseTester tester, PrepAndExpectedTestCase prepAndExpectedTestCase)
      Creates an executor for an annotation-driven test - equivalent to AnnotatedTestExecutor(AnnotatedTestConfiguration, IDatabaseTester, PrepAndExpectedTestCase, boolean) with annotationDriven true.
      Parameters:
      configuration - The resolved configuration to execute.
      tester - The tester to drive the setup/teardown path with, or to construct a PrepAndExpectedTestCase around when prepAndExpectedTestCase is null and the prep/expected path is configured.
      prepAndExpectedTestCase - An already-injected test case to drive instead of constructing one, or null to have this executor construct one from AnnotatedTestConfiguration.getPrepAndExpectedTestCaseClass().
    • AnnotatedTestExecutor

      public AnnotatedTestExecutor(AnnotatedTestConfiguration configuration, IDatabaseTester tester, PrepAndExpectedTestCase prepAndExpectedTestCase, boolean annotationDriven)
      Creates an executor for one test.

      Mutates tester only when annotationDriven is true: installOperationListener() runs here, replacing tester's IOperationListener with an ExecutorOperationListener wrapping the previous one. A binding constructs one executor per test method, so a tester shared across methods (e.g. a static @DbUnitTester field) is re-wrapped each time - ConnectionPreservingOperationListener.unwrap(IOperationListener) unwraps the prior wrapper first, so the layers do not stack - and the last test's wrapper stays installed on the tester after the class finishes, holding a reference to that last executor until the tester is itself discarded or given a new listener. When annotationDriven is false - the classic path - the tester's listener is left untouched; see the class Javadoc.

      Parameters:
      configuration - The resolved configuration to execute.
      tester - The tester to drive the setup/teardown path with, or to construct a PrepAndExpectedTestCase around when prepAndExpectedTestCase is null and the prep/expected path is configured.
      prepAndExpectedTestCase - An already-injected test case to drive instead of constructing one, or null to have this executor construct one from AnnotatedTestConfiguration.getPrepAndExpectedTestCaseClass().
      annotationDriven - Whether the test opted into the org.dbunit.annotation family - any @DbUnit* annotation, or a @DbUnitTester/@DbUnitTestCase field. False for a bare @ExtendWith(DbUnitExtension.class) class with one plain, unannotated IDatabaseTester field, whose tester listener and connection lifecycle are then left exactly as the 3.5.0 lifecycle. Always true for the prep/expected path, which needs @DbUnitExpected.
  • Method Details

    • getTester

      public IDatabaseTester getTester()
      Returns the tester this executor drives, so a binding can inject it as a parameter without resolving a second, independent instance.
      Returns:
      The tester passed to the constructor.
    • getPrepAndExpectedTestCase

      public PrepAndExpectedTestCase getPrepAndExpectedTestCase()
      Returns the PrepAndExpectedTestCase this executor drives, or null when AnnotatedTestConfiguration.isExpected() is false, or it is true but nothing has constructed one yet - a binding resolving a parameter before beforeTest() runs (e.g. a @BeforeEach parameter) sees null unless one was already injected through the constructor, or an earlier parameter resolution this same test already triggered ExpectedLifecycle.ensureTestCase() (e.g. a Connection parameter resolved via getConnection()).
      Returns:
      The test case this executor drives, or null if none exists yet.
    • getConnection

      public IDatabaseConnection getConnection() throws Exception
      Returns the connection this test's steps use, resolved at most once per test and reused by every caller instead of each asking independently - the row count check's own baseline/verify calls on the setup/teardown path, and a binding injecting an IDatabaseConnection/Connection parameter on either path. On the prep/expected path (AnnotatedTestConfiguration.isExpected() true), delegates to PrepAndExpectedTestCase.getReusableConnection() - constructing the test case first via ExpectedLifecycle.ensureTestCase() if it does not exist yet - instead of asking tester directly, so a parameter injection shares that test case's own connection; on the setup/teardown path, asks tester directly. For a tester with no connection caching of its own (e.g. a plain JdbcDatabaseTester), asking independently on either path would otherwise open one new physical connection per call. Closed in afterTest(boolean) - see the class Javadoc.
      Returns:
      The connection to reuse, or null if none is available (e.g. a test double).
      Throws:
      Exception - If resolving the connection fails.
    • beforeTest

      public void beforeTest() throws Exception
      Runs every before-test step by delegating to the path AnnotatedTestConfiguration.isExpected() selects: SetupTeardownLifecycle.before() or ExpectedLifecycle.before().
      Throws:
      Exception - If any step fails.
    • afterTest

      public void afterTest(boolean testFailed) throws Exception
      Runs every after-test step by delegating to the path AnnotatedTestConfiguration.isExpected() selects - SetupTeardownLifecycle.after(boolean) or ExpectedLifecycle.after(boolean) - then closes the connection testScopedConnection memoized, if one was ever resolved, so a difference reported by the row count check still leaves it closed.

      When both the step and closing the connection fail, the step's failure is the one thrown, with the close failure attached to it via Throwable.addSuppressed(Throwable) rather than replacing it - a plain finally block would otherwise let the close failure silently discard the more useful diagnostic (e.g. which table the row count check found unexpectedly changed). The step's failure is caught as Throwable, not just Exception, and rethrown with its original static type preserved: a comparison mismatch on the prep/expected path fails via DbComparisonFailure, an Error subclass, not an Exception - catching only Exception would skip closing the connection on every ordinary verification failure, the single most common way this method's step throws at all.

      Parameters:
      testFailed - Whether the test method itself already threw; when true, verification is skipped so a difference does not mask the real failure.
      Throws:
      Exception - If any step fails with a checked exception.
      Error - If any step fails with an Error, e.g. a comparison mismatch.