読者です 読者をやめる 読者になる 読者になる

かんがるーさんの日記

最近自分が興味をもったものを調べた時の手順等を書いています。今は Spring Boot をいじっています。

Spring Boot で書籍の貸出状況確認・貸出申請する Web アプリケーションを作る ( その55 )( TestDataResource クラスの機能追加2 )

概要

Spring Boot で書籍の貸出状況確認・貸出申請する Web アプリケーションを作る ( その54 )( TestDataResource クラスの機能追加 ) の続きです。

  • 今回の手順で確認できるのは以下の内容です。
    • TestDataResource クラスの機能追加・変更
      • ksbysample.common.test.rule.db パッケージ配下のクラスで dataSource.getConnection() で取得した Connection オブジェクトをクローズしていない問題を修正する。
      • @TestData アノテーション複数付加できるようにする。
      • TestDataResource クラス内の定数にルートパスを設定することで @BaseTestData, @TestData アノテーションではルートパスを除く部分のみ指定できるようにする。

参照したサイト・書籍

目次

  1. ksbysample.common.test.rule.db パッケージ配下のクラスで dataSource.getConnection() で取得した Connection オブジェクトをクローズしていない問題を修正する
  2. @TestData アノテーションを複数付加できるようにする
  3. TestDataResource クラス内の定数にルートパスを設定することで @BaseTestData, @TestData アノテーションではルートパスを除く部分のみ指定できるようにする
  4. 次回は。。。

手順

ksbysample.common.test.rule.db パッケージ配下のクラスで dataSource.getConnection() で取得した Connection オブジェクトをクローズしていない問題を修正する

  1. feature/92-issue ブランチを作成します。

  2. src/test/java/ksbysample/common/test/rule/db の下の DbUnitUtils.javaリンク先の内容 に変更します。

  3. src/test/java/ksbysample/common/test/rule/db の下の TestDataLoader.javaリンク先の内容 に変更します。

  4. src/test/java/ksbysample/common/test/rule/db の下の TestSqlExecutor.javaリンク先の内容 に変更します。

  5. src/test/java/ksbysample/common/test/rule/db の下の TableDataAssert.javaリンク先の内容 に変更します。

  6. src/test/java/ksbysample/common/test/rule/db の下の TestDataResource.javaリンク先のその1の内容 に変更します。

  7. 動作確認します。Gradle projects View から clean タスクの実行→「Rebuild Project」メニューの実行→build タスクの実行を行い、"BUILD SUCCESSFUL" のメッセージが出力されることを確認します。

    f:id:ksby:20160217200645p:plain

  8. Project View のルートでコンテキストメニューを表示して「Run 'All Tests' with Coverage」を選択し、テストが全て成功することを確認します。

    f:id:ksby:20160217201234p:plain

  9. commit、GitHub へ Push、feature/92-issue -> 1.0.x へ Pull Request、1.0.x でマージ、feature/92-issue ブランチを削除、をします。

@TestData アノテーション複数付加できるようにする

  1. feature/90-issue ブランチを作成します。

  2. src/test/java/ksbysample/common/test/rule/db の下に TestDataList.java を作成します。作成後、リンク先の内容 に変更します。

  3. src/test/java/ksbysample/common/test/rule/db の下の TestData.javaリンク先の内容 に変更します。

  4. src/test/java/ksbysample/common/test/rule/db の下の TestDataResource.javaリンク先のその2の内容 に変更します。

  5. src/test/resources/ksbysample/webapp/lending/web/confirmresult/testdata/001 の下の lending_book.csvリンク先の内容 に変更します。

動作確認します。src/test/java/ksbysample/webapp/lending の下の TestDataResourceTest.java に以下のテストクラスを追記します。

    @RunWith(SpringJUnit4ClassRunner.class)
    @SpringApplicationConfiguration(classes = Application.class)
    @WebAppConfiguration
    public static class テストクラス_TestData_Annotation {

        @Rule
        @Autowired
        public TestDataResource testDataResource;

        @Autowired
        private DataSource dataSource;

        @Test
        @TestData("src/test/resources/ksbysample/webapp/lending/web/lendingapp/testdata/001")
        public void テストメソッド_lendingapp() throws Exception {
            IDataSet dataSet = new CsvDataSet(
                    new File("src/test/resources/ksbysample/webapp/lending/web/lendingapp/testdata/001"));
            TableDataAssert tableDataAssert = new TableDataAssert(dataSet, dataSource);
            tableDataAssert.assertEquals("lending_app", new String[]{}, AssertOptions.EXCLUDE_COLUM);
            tableDataAssert.assertEquals("lending_book", new String[]{}, AssertOptions.EXCLUDE_COLUM);
        }

        @Test
        @TestData("src/test/resources/ksbysample/webapp/lending/web/confirmresult/testdata/001")
        public void テストメソッド_confirmresult() throws Exception {
            IDataSet dataSet = new CsvDataSet(
                    new File("src/test/resources/ksbysample/webapp/lending/web/confirmresult/testdata/001"));
            TableDataAssert tableDataAssert = new TableDataAssert(dataSet, dataSource);
            tableDataAssert.assertEquals("lending_app", new String[]{}, AssertOptions.EXCLUDE_COLUM);
            tableDataAssert.assertEquals("lending_book", new String[]{}, AssertOptions.EXCLUDE_COLUM);
        }

        @Test
        @TestData(order = 1, value = "src/test/resources/ksbysample/webapp/lending/web/lendingapp/testdata/001")
        @TestData(order = 2, value = "src/test/resources/ksbysample/webapp/lending/web/confirmresult/testdata/001")
        public void テストメソッド_lendingapp_confirmresult() throws Exception {
            IDataSet dataSet = new CsvDataSet(
                    new File("src/test/resources/ksbysample/webapp/lending/web/confirmresult/testdata/001"));
            TableDataAssert tableDataAssert = new TableDataAssert(dataSet, dataSource);
            tableDataAssert.assertEquals("lending_app", new String[]{}, AssertOptions.EXCLUDE_COLUM);
            tableDataAssert.assertEquals("lending_book", new String[]{}, AssertOptions.EXCLUDE_COLUM);
        }

        @Test
        @TestData(order = 2, value = "src/test/resources/ksbysample/webapp/lending/web/lendingapp/testdata/001")
        @TestData(order = 1, value = "src/test/resources/ksbysample/webapp/lending/web/confirmresult/testdata/001")
        public void テストメソッド_confirmresult_lendingapp() throws Exception {
            IDataSet dataSet = new CsvDataSet(
                    new File("src/test/resources/ksbysample/webapp/lending/web/lendingapp/testdata/001"));
            TableDataAssert tableDataAssert = new TableDataAssert(dataSet, dataSource);
            tableDataAssert.assertEquals("lending_app", new String[]{}, AssertOptions.EXCLUDE_COLUM);
            tableDataAssert.assertEquals("lending_book", new String[]{}, AssertOptions.EXCLUDE_COLUM);
        }

    }

テストクラス_TestData_Annotation の左側の矢印アイコンをクリックしてコンテキストメニューを表示し、「Run TestDataResourceTest$テストクラス_Te...」を選択してテストを実行します。

テストが成功することが確認できます。

f:id:ksby:20160217223429p:plain

commit、GitHub へ Push、feature/90-issue -> 1.0.x へ Pull Request、1.0.x でマージ、feature/90-issue ブランチを削除、をします。

TestDataResource クラス内の定数にルートパスを設定することで @BaseTestData, @TestData アノテーションではルートパスを除く部分のみ指定できるようにする

  1. feature/91-issue ブランチを作成します。

  2. src/test/java/ksbysample/common/test/rule/db の下の TestDataResource.javaリンク先のその3の内容 に変更します。

  3. Ctrl+Shift+R を押して「Replace in Path」ダイアログを表示し、「Text to find」に @TestData("src/test/resources/ksbysample/webapp/lending/ を、「Replace with」に @TestData(" を入力してから「Find」ボタンをクリックしてプロジェクト内のファイルの文字列を置換します。

    f:id:ksby:20160219005958p:plain

  4. 動作確認します。Gradle projects View から clean タスクの実行→「Rebuild Project」メニューの実行→build タスクの実行を行い、"BUILD SUCCESSFUL" のメッセージが出力されることを確認します。

    f:id:ksby:20160219012218p:plain

  5. Project View のルートでコンテキストメニューを表示して「Run 'All Tests' with Coverage」を選択し、テストが全て成功することを確認します。

    f:id:ksby:20160219012528p:plain

  6. commit、GitHub へ Push、feature/91-issue -> 1.0.x へ Pull Request、1.0.x でマージ、feature/91-issue ブランチを削除、をします。

次回は。。。

もう少しテスト用ライブラリの機能強化を行います。TestDataResource クラスはこれで終了で、次回は TableDataAssert クラスの機能追加を行います。

ソースコード

DbUnitUtils.java

package ksbysample.common.test.rule.db;

import org.dbunit.DatabaseUnitException;
import org.dbunit.database.DatabaseConfig;
import org.dbunit.database.DatabaseConnection;
import org.dbunit.database.IDatabaseConnection;

import java.sql.Connection;
import java.sql.SQLException;

public class DbUnitUtils {

    public static final String NULL_STRING = "[null]";

    public static IDatabaseConnection createDatabaseConnection(Connection connection)
            throws SQLException, DatabaseUnitException {
        IDatabaseConnection conn = new DatabaseConnection(connection);
        DatabaseConfig databaseConfig = conn.getConfig();
        databaseConfig.setProperty(DatabaseConfig.FEATURE_ALLOW_EMPTY_FIELDS, true);
        return conn;
    }

}
  • createDatabaseConnection メソッドの以下の点を変更します。
    • 引数を DataSource dataSourceConnection connection へ変更します。
    • new DatabaseConnection(...) の引数を dataSource.getConnection()connection へ変更します。

TestDataLoader.java

package ksbysample.common.test.rule.db;

import org.dbunit.database.IDatabaseConnection;
import org.dbunit.dataset.IDataSet;
import org.dbunit.dataset.ReplacementDataSet;
import org.dbunit.dataset.csv.CsvDataSet;
import org.dbunit.operation.DatabaseOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.sql.DataSource;
import java.io.File;
import java.sql.Connection;

@Component
public class TestDataLoader {

    @Autowired
    private DataSource dataSource;

    public void load(String csvDir) {
        IDatabaseConnection conn = null;
        try (Connection connection = dataSource.getConnection()) {
            conn = DbUnitUtils.createDatabaseConnection(connection);
            load(conn, csvDir);
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            try {
                if (conn != null) conn.close();
            } catch (Exception ignored) {
            }
        }
    }

    public void load(IDatabaseConnection conn, String csvDir) {
        try {
            IDataSet dataSet = new CsvDataSet(new File(csvDir));
            ReplacementDataSet replacementDataset = new ReplacementDataSet(dataSet);
            replacementDataset.addReplacementObject(DbUnitUtils.NULL_STRING, null);
            DatabaseOperation.CLEAN_INSERT.execute(conn, replacementDataset);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

}
  • public void load(IDatabaseConnection conn, String csvDir) メソッドを追加します。
  • public void load(String csvDir) メソッドの以下の点を変更します。
    • try の後に (Connection connection = dataSource.getConnection()) を追加します。
    • DbUnitUtils.createDatabaseConnection(...) の引数を dataSourceconnection に変更します。
    • その後の処理を load(conn, csvDir); に変更します。

TestSqlExecutor.java

    public void execute(Connection connection, Description description) throws SQLException {
        try (Statement stmt = connection.createStatement()) {
            // テストクラスに付加されている @BaseTestSql, @TestSql アノテーションの SQL を実行する
            Class<?> testClass = description.getTestClass();
            executeTestSqlListOrTestSql(stmt
                    , testClass.getAnnotation(this.testSqlListClass)
                    , testClass.getAnnotation(this.testSqlClass));

            // TestDataResource クラスのフィールドに付加されている @BaseTestSql, @TestSql アノテーションの SQL を実行する
            Field[] fields = description.getTestClass().getDeclaredFields();
            for (Field field : fields) {
                if (field.getType().equals(TestDataResource.class)) {
                    executeTestSqlListOrTestSql(stmt
                            , field.getAnnotation(this.testSqlListClass)
                            , field.getAnnotation(this.testSqlClass));
                }
            }

            // テストメソッドに付加されている @BaseTestSql, @TestSql アノテーションの SQL を実行する
            executeTestSqlListOrTestSql(stmt
                    , description.getAnnotation(this.testSqlListClass)
                    , description.getAnnotation(this.testSqlClass));
        }
    }

    ..........

    private void executeTestSqlList(Statement stmt, L testSqlList) {
        if (testSqlList != null) {
            Arrays.asList(value(testSqlList)).stream()
                    .sorted(comparing(testSql -> order(testSql)))
                    .forEach(testSql -> executeTestSql(stmt, testSql));
        }
    }
  • execute メソッドの以下の点を変更します。
    • メソッドの第1引数を DataSource dataSourceConnection connection へ変更します。
    • try の後の (...) の中の記述を Statement stmt = dataSource.getConnection().createStatement()Statement stmt = connection.createStatement() へ変更します。
  • executeTestSqlList メソッドの中の Stream API の forEach の中の記述から {} を削除します。

TableDataAssert.java

    private ITable actualTable(String tableName, String[] columnNames, AssertOptions options)
            throws DatabaseUnitException, SQLException {
        IDatabaseConnection conn = null;
        ITable table = null;
        try (Connection connection = this.dataSource.getConnection()) {
            conn = DbUnitUtils.createDatabaseConnection(connection);
            table = conn.createDataSet().getTable(tableName);
            if (columnNames != null) {
                table = columnFilter(table, columnNames, options);
            }
        } finally {
            try {
                if (conn != null) conn.close();
            } catch (Exception ignored) {
            }
        }
        return table;
    }
  • actualTable メソッドの以下の点を変更しました。
    • IDatabaseConnection conn = new DatabaseConnection(this.dataSource.getConnection()); と記述していて、かつ取得した IDatabaseConnection オブジェクトも close していなかったので、Connection オブジェクトは try-with-resources 構文内で取得して自動的にクローズするようにし、IDatabaseConnection オブジェクトの取得は new DatabaseConnection(...)DbUnitUtils.createDatabaseConnection(...) へ変更してクローズする処理を追加しました。

TestDataResource.java

■その1

    @Override
    protected void starting(Description description) {
        // @NouseTestDataResource アノテーションがテストメソッドに付加されていない場合には処理を実行する
        if (!hasNoUseTestDataResourceAnnotation(description)) {
            IDatabaseConnection conn = null;
            try (Connection connection = dataSource.getConnection()) {
                conn = DbUnitUtils.createDatabaseConnection(connection);

                // バックアップ&ロード&リストア対象のテストデータのパスを取得する
                String testDataBaseDir = getBaseTestDir(description);

                // バックアップを取得する
                backupDb(conn, testDataBaseDir);

                // テストデータをロードする
                testDataLoader.load(conn, testDataBaseDir);

                // @BaseTestSql アノテーションで指定された SQL を実行する
                TestSqlExecutor<BaseTestSqlList, BaseTestSql> baseTestSqlExecutor
                        = new TestSqlExecutor<>(BaseTestSqlList.class, BaseTestSql.class);
                baseTestSqlExecutor.execute(connection, description);

                // テストメソッドに @TestData アノテーションが付加されている場合には、
                // アノテーションで指定されたテストデータをロードする
                loadTestData(conn, description);

                // @TestSql アノテーションで指定された SQL を実行する
                TestSqlExecutor<TestSqlList, TestSql> testSqlExecutor
                        = new TestSqlExecutor<>(TestSqlList.class, TestSql.class);
                testSqlExecutor.execute(connection, description);
            } catch (Exception e) {
                throw new RuntimeException(e);
            } finally {
                try {
                    if (conn != null) conn.close();
                } catch (Exception ignored) {
                }
            }
        }
    }

    ..........

    private void loadTestData(IDatabaseConnection conn, Description description) {
        description.getAnnotations().stream()
                .filter(annotation -> annotation instanceof TestData)
                .forEach(annotation -> {
                    TestData testData = (TestData) annotation;
                    testDataLoader.load(conn, testData.value());
                });
    }
  • loadTestData メソッドの以下の点を変更します。
    • 第1引数に IDatabaseConnection conn を追加します。
    • testDataLoader.load(...) の第1引数に conn を追加します。
  • starting メソッドの以下の点を変更します。
    • try の後に (Connection connection = dataSource.getConnection()) を追加します。
    • if (!hasNoUseTestDataResourceAnnotation(description)) { ... } の記述を try (...) の外側に移動します。@NoUseTestDataResource アノテーションが付加されている場合には Connection オブジェクトを取得しないようにします。
    • DbUnitUtils.createDatabaseConnection(...)dataSourceconnection へ変更します。
    • testDataLoader.load(...) の第1引数に conn を追加します。
    • baseTestSqlExecutor.execute(...) の第1引数を dataSourceconnection へ変更します。
    • loadTestData(...) の第1引数に conn を追加します。
    • testSqlExecutor.execute(...) の第1引数を dataSourceconnection へ変更します。

■その2

    private void loadTestData(IDatabaseConnection conn, Description description) {
        description.getAnnotations().stream()
                .filter(annotation -> annotation instanceof TestDataList || annotation instanceof TestData)
                .forEach(annotation -> {
                    if (annotation instanceof TestDataList) {
                        TestDataList testDataList = (TestDataList) annotation;
                        Arrays.asList(testDataList.value()).stream()
                                .sorted(comparing(testData -> testData.order()))
                                .forEach(testData -> testDataLoader.load(conn, testData.value()));
                    } else {
                        TestData testData = (TestData) annotation;
                        testDataLoader.load(conn, testData.value());
                    }
                });
    }
  • loadTestData メソッドの以下の点を変更します。
    • .filter メソッドに指定する条件を annotation instanceof TestDataannotation instanceof TestDataList || annotation instanceof TestData に変更します。
    • .forEach メソッド内の処理を annotation が TestDataList クラスか TestData クラスかで処理を分けて、TestData クラスの方にはこれまでの処理を記述し、TestDataList クラスの方には order の昇順に @TestData アノテーションを処理するよう処理を追加します。

■その3

package ksbysample.common.test.rule.db;

import org.dbunit.DatabaseUnitException;
import org.dbunit.database.IDatabaseConnection;
import org.dbunit.database.QueryDataSet;
import org.dbunit.dataset.DataSetException;
import org.dbunit.dataset.IDataSet;
import org.dbunit.dataset.ReplacementDataSet;
import org.dbunit.dataset.xml.FlatXmlDataSet;
import org.dbunit.dataset.xml.FlatXmlDataSetBuilder;
import org.dbunit.operation.DatabaseOperation;
import org.junit.rules.TestWatcher;
import org.junit.runner.Description;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.sql.DataSource;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.net.MalformedURLException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;

import static java.util.Comparator.comparing;

@Component
public class TestDataResource extends TestWatcher {

    private static final String BASETESTDATA_ROOT_DIR = "src/test/resources/";
    private static final String TESTDATA_ROOT_DIR = "src/test/resources/ksbysample/webapp/lending/";
    private static final String BASETESTDATA_DIR = BASETESTDATA_ROOT_DIR + "testdata/base";
    private static final String BACKUP_FILE_NAME = "ksbylending_backup";

    @Autowired
    private DataSource dataSource;

    @Autowired
    private TestDataLoader testDataLoader;

    private File backupFile;

    @Override
    protected void starting(Description description) {
        // @NouseTestDataResource アノテーションがテストメソッドに付加されていない場合には処理を実行する
        if (!hasNoUseTestDataResourceAnnotation(description)) {
            IDatabaseConnection conn = null;
            try (Connection connection = dataSource.getConnection()) {
                conn = DbUnitUtils.createDatabaseConnection(connection);

                // バックアップ&ロード&リストア対象のテストデータのパスを取得する
                String testDataBaseDir = getBaseTestDir(description);

                // バックアップを取得する
                backupDb(conn, testDataBaseDir);

                // テストデータをロードする
                testDataLoader.load(conn, testDataBaseDir);

                // @BaseTestSql アノテーションで指定された SQL を実行する
                TestSqlExecutor<BaseTestSqlList, BaseTestSql> baseTestSqlExecutor
                        = new TestSqlExecutor<>(BaseTestSqlList.class, BaseTestSql.class);
                baseTestSqlExecutor.execute(connection, description);

                // テストメソッドに @TestData アノテーションが付加されている場合には、
                // アノテーションで指定されたテストデータをロードする
                loadTestData(conn, description);

                // @TestSql アノテーションで指定された SQL を実行する
                TestSqlExecutor<TestSqlList, TestSql> testSqlExecutor
                        = new TestSqlExecutor<>(TestSqlList.class, TestSql.class);
                testSqlExecutor.execute(connection, description);
            } catch (Exception e) {
                throw new RuntimeException(e);
            } finally {
                try {
                    if (conn != null) conn.close();
                } catch (Exception ignored) {
                }
            }
        }
    }

    @Override
    protected void finished(Description description) {
        IDatabaseConnection conn = null;
        try (Connection connection = dataSource.getConnection()) {
            // @NouseTestDataResource アノテーションがテストメソッドに付加されていない場合には処理を実行する
            if (!hasNoUseTestDataResourceAnnotation(description)) {
                conn = DbUnitUtils.createDatabaseConnection(connection);

                // バックアップからリストアする
                restoreDb(conn);
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            try {
                if (conn != null) conn.close();
            } catch (Exception ignored) {
            }

            if (backupFile != null) {
                try {
                    Files.delete(backupFile.toPath());
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
                backupFile = null;
            }
        }
    }

    private boolean hasNoUseTestDataResourceAnnotation(Description description) {
        Collection<Annotation> annotationList = description.getAnnotations();
        boolean result = annotationList.stream()
                .anyMatch(annotation -> annotation instanceof NoUseTestDataResource);
        return result;
    }

    private String getBaseTestDir(Description description) {
        // @BaseTestData アノテーションで指定されている場合にはそれを使用し、指定されていない場合には
        // BASETESTDATA_DIR 定数で指定されているものと使用する

        // テストメソッドに @BaseTestData アノテーションが付加されているかチェックする
        BaseTestData baseTestData = description.getAnnotation(BaseTestData.class);
        if (baseTestData != null) {
            return BASETESTDATA_ROOT_DIR + baseTestData.value();
        }

        // TestDataResource クラスのフィールドに @BaseTestData アノテーションが付加されているかチェックする
        Field[] fields = description.getTestClass().getDeclaredFields();
        for (Field field : fields) {
            if (field.getType().equals(TestDataResource.class)) {
                baseTestData = field.getAnnotation(BaseTestData.class);
                if (baseTestData != null) {
                    return BASETESTDATA_ROOT_DIR + baseTestData.value();
                }
            }
        }

        // テストクラスに @BaseTestData アノテーションが付加されているかチェックする
        Class<?> testClass = description.getTestClass();
        baseTestData = testClass.getAnnotation(BaseTestData.class);
        if (baseTestData != null) {
            return BASETESTDATA_ROOT_DIR + baseTestData.value();
        }

        return BASETESTDATA_DIR;
    }

    private void backupDb(IDatabaseConnection conn, String testDataBaseDir)
            throws DataSetException, IOException {
        QueryDataSet partialDataSet = new QueryDataSet(conn);

        // BASETESTDATA_DIR で指定されたディレクトリ内の table-ordering.txt に記述されたテーブル名一覧を取得し、
        // バックアップテーブルとしてセットする
        List<String> backupTableList = Files.readAllLines(Paths.get(testDataBaseDir, "table-ordering.txt"));
        for (String backupTable : backupTableList) {
            partialDataSet.addTable(backupTable);
        }

        ReplacementDataSet replacementDatasetBackup = new ReplacementDataSet(partialDataSet);
        replacementDatasetBackup.addReplacementObject(null, DbUnitUtils.NULL_STRING);
        this.backupFile = File.createTempFile(BACKUP_FILE_NAME, "xml");
        try (FileOutputStream fos = new FileOutputStream(this.backupFile)) {
            FlatXmlDataSet.write(replacementDatasetBackup, fos);
        }
    }

    private void restoreDb(IDatabaseConnection conn)
            throws MalformedURLException, DatabaseUnitException, SQLException {
        if (this.backupFile != null) {
            IDataSet dataSet = new FlatXmlDataSetBuilder().build(this.backupFile);
            ReplacementDataSet replacementDatasetRestore = new ReplacementDataSet(dataSet);
            replacementDatasetRestore.addReplacementObject(DbUnitUtils.NULL_STRING, null);
            DatabaseOperation.CLEAN_INSERT.execute(conn, replacementDatasetRestore);
        }
    }

    private void loadTestData(IDatabaseConnection conn, Description description) {
        description.getAnnotations().stream()
                .filter(annotation -> annotation instanceof TestDataList || annotation instanceof TestData)
                .forEach(annotation -> {
                    if (annotation instanceof TestDataList) {
                        TestDataList testDataList = (TestDataList) annotation;
                        Arrays.asList(testDataList.value()).stream()
                                .sorted(comparing(testData -> testData.order()))
                                .forEach(testData -> testDataLoader.load(conn, TESTDATA_ROOT_DIR + testData.value()));
                    } else {
                        TestData testData = (TestData) annotation;
                        testDataLoader.load(conn, TESTDATA_ROOT_DIR + testData.value());
                    }
                });
    }

}
  • 定数 BASETESTDATA_ROOT_DIR, TESTDATA_ROOT_DIR を追加します。
  • TESTDATA_BASE_DIRBASETESTDATA_DIR へ変更し、セットする値を BASETESTDATA_ROOT_DIR + "testdata/base" へ変更します。
  • getBaseTestDir メソッドの以下の点を変更します。
    • 途中3ヶ所の return 文で返す値を baseTestData.value()BASETESTDATA_ROOT_DIR + baseTestData.value() へ変更します。
    • 最後の return 文で返す値を TESTDATA_BASE_DIRBASETESTDATA_DIR へ変更します。
  • loadTestData メソッドの以下の点を変更します。
    • 2ヶ所ある testDataLoader.load(conn, testData.value());testDataLoader.load(conn, TESTDATA_ROOT_DIR + testData.value()); へ変更します。

TestDataList.java

package ksbysample.common.test.rule.db;

import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;

import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

@Target({METHOD})
@Retention(RUNTIME)
@Documented
public @interface TestDataList {

    TestData[] value();

}

TestData.java

package ksbysample.common.test.rule.db;

import java.lang.annotation.Documented;
import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;

import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

@Target({METHOD})
@Retention(RUNTIME)
@Documented
@Repeatable(TestDataList.class)
public @interface TestData {

    long order() default 1;

    String value();

}
  • @Target アノテーションの値の指定を {} で囲み、ElementType.METHOD を import static して METHOD に変更しました。
  • RetentionPolicy.RUNTIME を static import して RUNTIME に変更しました。
  • @Documented を追加しました。
  • @Repeatable(TestDataList.class) を追加しました。
  • long order() default 1; を追加しました。

web/confirmresult/testdata/001/lending_book.csv

lending_book_id,lending_app_id,isbn,book_name,lending_state,lending_app_flg,lending_app_reason,approval_result,approval_reason,version
521,105,978-4-7741-6366-6,GitHub実践入門,蔵書なし,[null],[null],[null],[null],1
522,105,978-4-7741-5377-3,JUnit実践入門,蔵書あり,1,開発で使用する為,2,購入済です,2
523,105,978-4-7973-8014-9,Java最強リファレンス,蔵書あり,,[null],[null],[null],1
524,105,978-4-7973-4778-4,アジャイルソフトウェア開発の奥義,蔵書あり,1,勉強の為,1,,2
525,105,978-4-87311-704-1,Javaによる関数型プログラミング,蔵書あり,1,勉強会の調査の為,1,,2
  • lending_book_id の昇順に並び替えます。

履歴

2016/02/19
初版発行。