All Downloads are FREE. Search and download functionalities are using the official Maven repository.

name.didier.david.test4j.dbunit.DbUnitTestSupport Maven / Gradle / Ivy

The newest version!
package name.didier.david.test4j.dbunit;

import static java.util.Arrays.stream;

import static name.didier.david.check4j.ConciseCheckers.checkNotNull;

import javax.sql.DataSource;

import org.dbunit.DataSourceDatabaseTester;
import org.dbunit.DefaultPrepAndExpectedTestCase;
import org.dbunit.IDatabaseTester;
import org.dbunit.PrepAndExpectedTestCase;
import org.dbunit.PrepAndExpectedTestCaseSteps;
import org.dbunit.VerifyTableDefinition;
import org.dbunit.util.fileloader.DataFileLoader;
import org.dbunit.util.fileloader.FlatXmlDataFileLoader;

/**
 * Support class dealing with database testing with DbUnit.
 *
 * @author ddidier
 */
public class DbUnitTestSupport {

    /** The {@link DataSource} used to connect to the database. */
    private final DataSource dataSource;
    /** The DbUnit test case. */
    private final PrepAndExpectedTestCase testCase;

    /**
     * @param dataSource the {@link DataSource} used to connect to the database.
     */
    public DbUnitTestSupport(DataSource dataSource) {
        this.dataSource = checkNotNull(dataSource, "dataSource");
        this.testCase = createTestCase();
    }

    /**
     * @return the {@link DataSource} used to connect to the database.
     */
    public DataSource getDataSource() {
        return dataSource;
    }

    /**
     * Seed the data, run the test then check the data. See {@link PrepAndExpectedTestCase}.
     *
     * @param seedDataPaths the paths of the data files to load as seed data.
     * @param expectedDataPaths the paths of the data files to load as expected data.
     * @param verifiedTables the table definitions to verify after test execution.
     * @param testSteps the test steps to run.
     * @return a user defined object from running the test steps.
     */
    public Object test(
            String[] seedDataPaths,
            String[] expectedDataPaths,
            VerifyTableDefinition[] verifiedTables,
            TestStepsWithResult testSteps) {
        return doTest(seedDataPaths, expectedDataPaths, verifiedTables,
                (PrepAndExpectedTestCaseSteps) () -> testSteps.run());
    }

    /**
     * Seed the data, run the test then check the data. This is a convenience method when there is not need for
     * testSteps to return a value. See {@link PrepAndExpectedTestCase}.
     *
     * @param seedDataPaths the paths of the data files to load as seed data.
     * @param expectedDataPaths the paths of the data files to load as expected data.
     * @param verifiedTables the table definitions to verify after test execution.
     * @param testSteps the test steps to run.
     */
    public void test(
            String[] seedDataPaths,
            String[] expectedDataPaths,
            VerifyTableDefinition[] verifiedTables,
            TestStepsWithoutResult testSteps) {
        doTest(seedDataPaths, expectedDataPaths, verifiedTables, (PrepAndExpectedTestCaseSteps) () -> {
            testSteps.run();
            return null;
        });
    }

    /**
     * Seed the data, run the read-only test then check the data. See {@link PrepAndExpectedTestCase}.
     *
     * @param seedDataPaths the paths of the data files to load as seed data and expected data.
     * @param verifiedTables the table definitions to verify after test execution.
     * @param testSteps the test steps to run.
     * @return a user defined object from running the test steps.
     */
    public Object testReadOnly(
            String[] seedDataPaths,
            VerifyTableDefinition[] verifiedTables,
            TestStepsWithResult testSteps) {
        return test(seedDataPaths, seedDataPaths, verifiedTables, testSteps);
    }

    /**
     * Seed the data, run the read-only test then check the data. This is a convenience method when there is not need
     * for testSteps to return a value. See {@link PrepAndExpectedTestCase}.
     *
     * @param seedDataPaths the paths of the data files to load as seed data and expected data.
     * @param verifiedTables the table definitions to verify after test execution.
     * @param testSteps the test steps to run.
     */
    public void testReadOnly(
            String[] seedDataPaths,
            VerifyTableDefinition[] verifiedTables,
            TestStepsWithoutResult testSteps) {
        test(seedDataPaths, seedDataPaths, verifiedTables, testSteps);
    }

    /**
     * Defines database tables to verify (assert on data), including all columns.
     *
     * @param tableNames the names of the tables to verify.
     * @return database tables to verify
     */
    public static VerifyTableDefinition[] verifyTables(String... tableNames) {
        return stream(tableNames)
                .map(name -> new VerifyTableDefinition(name, new String[0]))
                .toArray(VerifyTableDefinition[]::new);
    }

    /**
     * @return a new test case using a {@link DataSourceDatabaseTester} and a {@link FlatXmlDataFileLoader}.
     */
    protected PrepAndExpectedTestCase createTestCase() {
        IDatabaseTester dbTester = new DataSourceDatabaseTester(dataSource);
        DataFileLoader loader = new FlatXmlDataFileLoader();
        return new DefaultPrepAndExpectedTestCaseExtension(loader, dbTester);
    }

    /**
     * Seed the data, run the test then check the data.
     *
     * @param seedDataPaths the paths of the data files to load as seed data.
     * @param expectedDataPaths the paths of the data files to load as expected data.
     * @param verifiedTables the table definitions to verify after test execution.
     * @param testSteps the test steps to run.
     * @return a user defined object from running the test steps.
     */
    protected Object doTest(
            String[] seedDataPaths,
            String[] expectedDataPaths,
            VerifyTableDefinition[] verifiedTables,
            PrepAndExpectedTestCaseSteps testSteps) {
        try {
            return testCase.runTest(verifiedTables, seedDataPaths, expectedDataPaths, testSteps);
        } catch (AssertionError e) {
            throw e;
        } catch (Throwable e) {
            // OK to catch Throwable here (for assertions)
            throw new RuntimeException("Error while testing database state", e);
        }
    }

    // -----------------------------------------------------------------------------------------------------------------

    /**
     * Test steps definition without returned value (void). Helpful for Java lambdas.
     *
     * @author ddidier
     */
    @FunctionalInterface
    public interface TestStepsWithoutResult {

        /**
         * Run the specific test steps.
         *
         * @throws Exception if an error occurs.
         */
        void run()
                throws Exception;
    }

    /**
     * Test steps definition with returned value. Helpful for Java lambdas.
     *
     * @author ddidier
     */
    @FunctionalInterface
    public interface TestStepsWithResult {

        /**
         * Run the specific test steps.
         *
         * @return a user specific object.
         * @throws Exception if an error occurs.
         */
        Object run()
                throws Exception;
    }

    /**
     * Do not swallow {@link AssertionError}s raised in the test steps.
     *
     * @author ddidier
     */
    public static class DefaultPrepAndExpectedTestCaseExtension
            extends DefaultPrepAndExpectedTestCase {

        /**
         * @param dataFileLoader load the data files.
         * @param databaseTester manipulate database.
         */
        public DefaultPrepAndExpectedTestCaseExtension(DataFileLoader dataFileLoader, IDatabaseTester databaseTester) {
            super(dataFileLoader, databaseTester);
        }

        /**
         * Seed the data, run the test then check the data. Do not swallow raised {@link AssertionError}s.
         *
         * @param seedDataPaths the paths of the data files to load as seed data.
         * @param expectedDataPaths the paths of the data files to load as expected data.
         * @param verifiedTables the table definitions to verify after test execution.
         * @param testSteps the test steps to run.
         * @return a user defined object from running the test steps.
         */
        @Override
        public Object runTest(
                VerifyTableDefinition[] verifiedTables,
                String[] seedDataPaths,
                String[] expectedDataPaths,
                PrepAndExpectedTestCaseSteps testSteps)
                throws Exception {

            Throwable throwable = null;

            try {
                preTest(verifiedTables, seedDataPaths, expectedDataPaths);
                return testSteps.run();
            } catch (final Throwable t) {
                // OK to catch Throwable here (for assertions)
                throwable = t;
                throw t;
            } finally {
                // do not test expected data if an exception was raised
                // since the comparison itself may raise an exception
                // which will hide the former one
                postTest(throwable == null);
            }
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy