Package org.dbunit

Class MultiDataSourcePrepAndExpectedTestCase

java.lang.Object
org.dbunit.MultiDataSourcePrepAndExpectedTestCase

public class MultiDataSourcePrepAndExpectedTestCase extends Object
Preps and verifies tables across multiple IDatabaseConnections around one run of the code under test, by fanning the PrepAndExpectedTestCase lifecycle out to one delegate per data source.

Holds an ordered {dataSourceName -> PrepAndExpectedTestCase} map, one delegate per data source - normally a DefaultPrepAndExpectedTestCase bound to its own IDatabaseTester. preTest(Map) sets each involved delegate up in the map's declared order; postTest(boolean) verifies and cleans each up in reverse order, collecting every delegate's failure into one MultiDataSourceAssertionError instead of stopping at the first; runTest(Map, PrepAndExpectedTestCaseSteps) runs the test steps exactly once in between. This class does not implement PrepAndExpectedTestCase itself - that interface's singular accessors and lifecycle methods have no honest answer for N data sources - and it drives each delegate only through that interface's own public preTest/postTest methods, never a reimplemented verify or cleanup.

The data source name to PrepAndExpectedTestCase wiring is fixed for this instance's life: assemble it with the constructors, from(String, PrepAndExpectedTestCase), the forTesters(...) factories, and add(String, PrepAndExpectedTestCase) / add(String, IDatabaseTester) / addAll(Map), all of which return this instance so they chain; the wiring is then frozen on the first preTest(Map) / runTest(Map, PrepAndExpectedTestCaseSteps) call, and a further add throws IllegalStateException. The {dataSourceName -> PrepAndExpectedTestData} map each test run passes to preTest(Map) / runTest(Map, PrepAndExpectedTestCaseSteps) varies per call; a wired data source absent from that map, or mapped to the exact PrepAndExpectedTestData.NONE instance, sits that run out entirely - its connection is never opened.

This instance is not thread-safe, but is safe to reuse sequentially - across @ParameterizedTest rows, or as a longer-lived singleton reused across test classes - since each preTest(Map) / runTest(Map, PrepAndExpectedTestCaseSteps) / postTest(boolean) call, including its own teardown, completes before the next begins.

Since:
3.6.0
Author:
Jeff Jensen
  • Constructor Details

    • MultiDataSourcePrepAndExpectedTestCase

      public MultiDataSourcePrepAndExpectedTestCase()
      Creates an empty instance for the delegate currency: assemble it with add(String, PrepAndExpectedTestCase) / addAll(Map).
    • MultiDataSourcePrepAndExpectedTestCase

      public MultiDataSourcePrepAndExpectedTestCase(Map<String,? extends PrepAndExpectedTestCase> testCasesByDataSourceName)
      Creates an instance seeded with the given delegates, in the given map's iteration order.
      Parameters:
      testCasesByDataSourceName - The delegates to seed this instance with, keyed by data source name; not null, and not containing a null or blank key or a null value. Defensively copied; mutating this map afterward does not affect this instance.
      Throws:
      IllegalArgumentException - If testCasesByDataSourceName is null, or contains a null or blank key, or a null value.
  • Method Details

    • from

      public static MultiDataSourcePrepAndExpectedTestCase from(String dataSourceName, PrepAndExpectedTestCase testCase)
      Creates an instance seeded with one data source, for the delegate currency.
      Parameters:
      dataSourceName - The data source name; not null or blank.
      testCase - The delegate for that data source; not null.
      Returns:
      The new instance, so further add/addAll calls can chain off it.
      Throws:
      IllegalArgumentException - If dataSourceName is null or blank, or if testCase is null.
    • forTesters

      public static MultiDataSourcePrepAndExpectedTestCase forTesters(DataFileLoader dataFileLoader)
      Creates an empty instance for the tester currency - add(String, IDatabaseTester) wraps each tester in a DefaultPrepAndExpectedTestCase sharing the given loader, with closeConnectionAfterTest true.
      Parameters:
      dataFileLoader - The loader every add(String, IDatabaseTester)-wrapped delegate shares.
      Returns:
      The new instance.
    • forTesters

      public static MultiDataSourcePrepAndExpectedTestCase forTesters(DataFileLoader dataFileLoader, boolean closeConnectionAfterTest)
      Creates an empty instance for the tester currency, as forTesters(DataFileLoader) does, with the given closeConnectionAfterTest applied to every add(String, IDatabaseTester)-wrapped delegate.
      Parameters:
      dataFileLoader - The loader every add(String, IDatabaseTester)-wrapped delegate shares.
      closeConnectionAfterTest - Whether each add(String, IDatabaseTester)-wrapped delegate closes its connection after each test; false to reuse each delegate's connection across @ParameterizedTest rows, as for a lone DefaultPrepAndExpectedTestCase.
      Returns:
      The new instance.
    • forTesters

      public static MultiDataSourcePrepAndExpectedTestCase forTesters(DataFileLoader dataFileLoader, Map<String,? extends IDatabaseTester> testersByDataSourceName)
      Creates an instance for the tester currency, seeded with the given testers - as forTesters(DataFileLoader) does, then add(String, IDatabaseTester) for each entry, in the given map's iteration order.
      Parameters:
      dataFileLoader - The loader every wrapped delegate shares.
      testersByDataSourceName - The testers to seed this instance with, keyed by data source name; not null.
      Returns:
      The new instance.
      Throws:
      IllegalArgumentException - If testersByDataSourceName is null, or contains a null or blank key, or a null value.
    • forTesters

      public static MultiDataSourcePrepAndExpectedTestCase forTesters(DataFileLoader dataFileLoader, Map<String,? extends IDatabaseTester> testersByDataSourceName, boolean closeConnectionAfterTest)
      Creates an instance for the tester currency, seeded with the given testers, as forTesters(DataFileLoader, Map) does, with the given closeConnectionAfterTest applied to every wrapped delegate.
      Parameters:
      dataFileLoader - The loader every wrapped delegate shares.
      testersByDataSourceName - The testers to seed this instance with, keyed by data source name; not null.
      closeConnectionAfterTest - Whether each wrapped delegate closes its connection after each test.
      Returns:
      The new instance.
      Throws:
      IllegalArgumentException - If testersByDataSourceName is null, or contains a null or blank key, or a null value.
    • add

      Wires one data source to the given delegate.
      Parameters:
      dataSourceName - The data source name; not null or blank, and not already wired.
      testCase - The delegate for that data source; not null.
      Returns:
      This instance, so calls can chain.
      Throws:
      IllegalArgumentException - If dataSourceName is null, blank, or already wired, or if testCase is null.
      IllegalStateException - If the wiring is already frozen by an earlier preTest(Map) / runTest(Map, PrepAndExpectedTestCaseSteps) call.
    • add

      public MultiDataSourcePrepAndExpectedTestCase add(String dataSourceName, IDatabaseTester tester)
      Wires one data source to a new DefaultPrepAndExpectedTestCase bound to the given tester, sharing this instance's loader and closeConnectionAfterTest.
      Parameters:
      dataSourceName - The data source name; not null or blank, and not already wired.
      tester - The tester for that data source; not null.
      Returns:
      This instance, so calls can chain.
      Throws:
      IllegalArgumentException - If dataSourceName is null, blank, or already wired, or if tester is null.
      IllegalStateException - If this instance was not created via one of the forTesters(...) factories, or if the wiring is already frozen.
    • addAll

      public MultiDataSourcePrepAndExpectedTestCase addAll(Map<String,? extends PrepAndExpectedTestCase> testCasesByDataSourceName)
      Wires every entry of the given map, in its iteration order.
      Parameters:
      testCasesByDataSourceName - The delegates to add, keyed by data source name; not null.
      Returns:
      This instance, so calls can chain.
      Throws:
      IllegalArgumentException - If testCasesByDataSourceName is null, or contains a null or blank key, an already-wired key, or a null value.
      IllegalStateException - If the wiring is already frozen by an earlier preTest(Map) / runTest(Map, PrepAndExpectedTestCaseSteps) call.
    • runTest

      public Object runTest(Map<String,PrepAndExpectedTestData> dataByDataSourceName, PrepAndExpectedTestCaseSteps testSteps) throws Exception
      Runs the full lifecycle once: preTest(Map), then the given test steps exactly once, then postTest(true).

      A throwable from the test steps is rethrown as-is - not wrapped - with every involved delegate torn down first via postTest(false) in reverse declared order, each teardown failure attached to the step throwable via Throwable.addSuppressed(Throwable) - except a teardown failure that is the exact same instance as the step throwable itself, which is dropped rather than attempting the self-suppression Throwable.addSuppressed(Throwable) forbids.

      Parameters:
      dataByDataSourceName - The prep/expected/verify data for this run, keyed by data source name; a wired data source absent from this map, or mapped to PrepAndExpectedTestData.NONE, sits this run out entirely.
      testSteps - The test steps to run, exactly once.
      Returns:
      The user-defined object the test steps returned.
      Throws:
      Exception - If preTest(Map) fails, if the test steps fail, or if postTest(boolean) fails.
    • preTest

      public void preTest(Map<String,PrepAndExpectedTestData> dataByDataSourceName) throws Exception
      Sets up every involved data source's prep data, in declared order.

      A wired data source absent from dataByDataSourceName, or mapped to the exact PrepAndExpectedTestData.NONE instance, is skipped entirely this run - its preTest is never called and its connection is never opened. An unknown key in dataByDataSourceName - one not wired to this instance - is rejected before any delegate is touched.

      If an involved delegate's own preTest fails, every involved delegate set up so far - including the one that just failed - is rolled back, via postTest(false), in reverse order, never a bare cleanupData() - with each rollback failure attached to the original failure via Throwable.addSuppressed(Throwable), except a rollback failure that is the exact same instance as the original failure, which is dropped rather than attempting the self-suppression Throwable.addSuppressed(Throwable) forbids; the original failure is then rethrown as-is.

      Parameters:
      dataByDataSourceName - The prep/expected/verify data for this run, keyed by data source name; not null, and containing no key this instance has no delegate wired for.
      Throws:
      IllegalStateException - If no data source is wired yet.
      IllegalArgumentException - If dataByDataSourceName is null, contains an unknown data source name, or maps a wired data source name to null.
      Exception - If an involved delegate's own preTest fails.
    • postTest

      public void postTest(boolean verifyData) throws Exception
      Verifies and cleans up every involved data source, in reverse declared order.

      Every involved delegate's own PrepAndExpectedTestCase.postTest(boolean) runs - verify (or, when verifyData is false, its own row count baseline discard) and cleanup - even if an earlier one in this reverse order failed; every collected failure is then aggregated into one MultiDataSourceAssertionError rather than the first one stopping the rest from being torn down.

      Parameters:
      verifyData - True to verify each involved delegate's data before cleaning it up; false to skip verification (the test steps already failed) and just clean up.
      Throws:
      MultiDataSourceAssertionError - If one or more involved delegates failed their own postTest.
      Exception
    • getDataSourceNames

      public Set<String> getDataSourceNames()
      Returns the wired data source names, in declared order.
      Returns:
      An unmodifiable view of the wired data source names, in declared order.
    • getTestCase

      public PrepAndExpectedTestCase getTestCase(String dataSourceName)
      Returns the delegate wired to one data source, for a test that needs to reach one directly - e.g. to assert something extra on it, or to inspect it after a failure.
      Parameters:
      dataSourceName - The data source name.
      Returns:
      The delegate wired to that data source.
      Throws:
      IllegalArgumentException - If no data source with that name is wired.