このエントリーをはてなブックマークに追加

DBUnit

DBUnitとは

JUnit試験にてDBへの事前データの登録や、登録・更新・削除後のテーブルの状態を期待値との比較を容易にするためのライブラリ

セットアップ

mavenを利用してセットアップを実施

https://mvnrepository.com/artifact/org.dbunit/dbunit

<!-- https://mvnrepository.com/artifact/org.dbunit/dbunit -->
<dependency>
    <groupId>org.dbunit</groupId>
    <artifactId>dbunit</artifactId>
    <version>2.7.0</version>
    <scope>test</scope>
</dependency>

利用方法

主な利用シーンとして以下の2つのシーンで利用します。

  • データの事前登録処理

    検索、更新、削除のSQLを実行する際に対象のデータを登録するための処理

    Connection con = DriverManager.getConnection(url, user, passwd);
    IDatabaseConnection dbconn = new DatabaseConnection(con);
    
    // データの事前登録処理
    File dataExcel = new File(getClass().getResource("/sample_table_data.xlsx").getPath());
    IDataSet dataset = new XlsDataSet(dataExcel);
    
    // ExcelのデータをDBへ適用する
    DatabaseOperation.CLEAN_INSERT.execute(dbconn, dataset);
    
  • DBデータとの比較処理

    登録、更新、削除後のデータベースの状態と期待値のExcelの比較するための処理

    // 期待値を取得
    File dataExcel = new File(getClass().getResource("/sample_table_data_insert_result.xlsx").getPath());
    IDataSet expectedDataSet = new XlsDataSet(dataExcel);
    ITable expectedTable = expectedDataSet.getTable(tableName);
    
    // 実際値を取得
    IDatabaseConnection dbconn = new DatabaseConnection(con);
    ITable actualTable = dbconn.createDataSet().getTable(tableName);
    

サンプル

今回はpostgresql用のテーブルに対してSQLを発行するためのDaoに対してテストケースを作成することで利用方法を確認します。

サンプル用のソース : https://github.com/snowhiro/dbunit-exsample

確認用テーブルの作成

create table sample_table (
    id bigint,
    doc_text text,
    now_time timestamp
)
ALTER TABLE sample_table ADD PRIMARY KEY(id);

検索メソッドの場合

対象のメソッド

public Optional<SampleTable> findSampleTable(Long id) {
    String selectSql = "SELECT * FROM sample_table where id = ?";
    try (PreparedStatement stmt = con.prepareStatement(selectSql)) {
        stmt.setLong(1, id);
        ResultSet rs = stmt.executeQuery();
        SampleTable st = null;
        while(rs.next()) {
            st = new SampleTable();
            st.setId(rs.getLong("id"));
            st.setDocText(rs.getString("doc_text"));
            st.setNowTime(rs.getTimestamp("now_time"));
        }
        return Optional.ofNullable(st);
    } catch (Exception e) {
        throw new RuntimeException("Daoの実行でエラーが発生しました。", e);
    }
}

テストケース

@Test
public void findSampleTable() throws Exception {

    IDatabaseConnection dbconn = new DatabaseConnection(con);

    // データの事前登録処理
    File dataExcel = new File(getClass().getResource("/sample_table_data.xlsx").getPath());
    IDataSet dataset = new XlsDataSet(dataExcel);

    // ExcelのデータをDBへ適用する
    DatabaseOperation.CLEAN_INSERT.execute(dbconn, dataset);

    // 処理を実行する
    Long id = 10L;
    SampleTable st = dao.findSampleTable(id).get();

    // 検証処理
    assertEquals("10番目のデータ", st.getDocText());
    LocalDateTime d1 = LocalDateTime.of(2020, 05, 05, 10, 30, 01);
    Timestamp exNowTime = new Timestamp(Date.from(d1.atZone(ZoneId.systemDefault()).toInstant()).getTime());
    assertEquals(exNowTime, st.getNowTime());

}

初期登録用のExcel

../../_images/dbunit01.png

登録メソッドの場合

public int insertSampleTable(SampleTable st) {
    String insertSql = "insert into sample_table (id, doc_text, now_time) values (?, ?, ?)";
    try (PreparedStatement stmt = con.prepareStatement(insertSql)) {
        stmt.setLong(1, st.getId());
        stmt.setString(2, st.getDocText());
        stmt.setTimestamp(3, st.getNowTime());
        return stmt.executeUpdate();
    } catch (Exception e) {
        throw new RuntimeException("Daoの実行でエラーが発生しました。", e);
    }
}
@Test
public void insertSampleTable() throws Exception {


    SampleTable st = new SampleTable();
    st.setId(100L);
    st.setDocText("テスト用のインサートデータ");
    st.setNowTime(new Timestamp(Date.from(LocalDateTime.now().atZone(ZoneId.systemDefault()).toInstant()).getTime()));
    int insertCount = dao.insertSampleTable(st);

    assertEquals(1, insertCount);

    String tableName = "sample_table";

    // 期待値を取得
    File dataExcel = new File(getClass().getResource("/sample_table_data_insert_result.xlsx").getPath());
    IDataSet expectedDataSet = new XlsDataSet(dataExcel);
    ITable expectedTable = expectedDataSet.getTable(tableName);

    // 実際値を取得
    IDatabaseConnection dbconn = new DatabaseConnection(con);
    ITable actualTable = dbconn.createDataSet().getTable(tableName);

    // 全てのカラムを比較する場合(今回の場合システム日時の登録のため必ずエラーとなる)
    //Assertion.assertEquals(expectedTable, actualTable);

    // システム日時の登録カラムなど除外用のカラムを追加する場合 フィルターを設定する
    String[] filterColumns = {"now_time"};
    ITable expectedFilterTable = DefaultColumnFilter.excludedColumnsTable(expectedTable, filterColumns);
    ITable actualFilterTable = DefaultColumnFilter.excludedColumnsTable(actualTable, filterColumns);
    // 比較
    Assertion.assertEquals(expectedFilterTable, actualFilterTable);
}

期待値の用のExcelファイル

../../_images/dbunit02.png

更新メソッドの場合

public int updateSampleTable(SampleTable st) {
    String insertSql = "update sample_table set doc_text = ? , now_time = ? where id = ?";
    try (PreparedStatement stmt = con.prepareStatement(insertSql)) {

        stmt.setString(1, st.getDocText());
        stmt.setTimestamp(2, st.getNowTime());

        stmt.setLong(3, st.getId());

        return stmt.executeUpdate();
    } catch (Exception e) {
        throw new RuntimeException("Daoの実行でエラーが発生しました。", e);
    }
}
@Test
public void updateSampleTable() throws Exception {

    IDatabaseConnection dbconn = new DatabaseConnection(con);

    // データの事前登録処理
    File dataExcel = new File(getClass().getResource("/sample_table_data.xlsx").getPath());
    IDataSet dataset = new XlsDataSet(dataExcel);

    // ExcelのデータをDBへ適用する
    DatabaseOperation.CLEAN_INSERT.execute(dbconn, dataset);

    SampleTable st = new SampleTable();
    st.setId(10L);
    st.setDocText("テスト用の更新データ");
    LocalDateTime d1 = LocalDateTime.of(2020, 05, 05, 12, 20, 52);
    st.setNowTime(new Timestamp(Date.from(d1.atZone(ZoneId.systemDefault()).toInstant()).getTime()));
    int updateCount = dao.updateSampleTable(st);

    assertEquals(1, updateCount);

    String tableName = "sample_table";

    // 期待値を取得
    File exExcel = new File(getClass().getResource("/sample_table_data_update_result.xlsx").getPath());
    IDataSet expectedDataSet = new XlsDataSet(exExcel);
    ITable expectedTable = expectedDataSet.getTable(tableName);

    // 実際値を取得
    ITable actualTable = dbconn.createDataSet().getTable(tableName);

    // 全てのカラムを比較
    Assertion.assertEquals(expectedTable, actualTable);

}

初期登録用のExcel

../../_images/dbunit01.png

期待値の用のExcelファイル

../../_images/dbunit03.png

メリット

  • ・試験時データの外出しが容易に行える
    (フォーマットとしては「CSV」「XML」「excel」の3種類)
  • ・insert文で記載する方式に比べてメンテナンス性が良い。
  • ・DBの比較処理を容易に実施することができる。登録、更新、削除等のDBの状態を指定ファイルに記載することで比較可能

デメリット

  • ・データのパースを実施する分試験実施時の速度は低下する。
  • ・フォーマットにexcelを利用する場合、excel環境が必要になる。
  • ・excelのシート名の制約があるため長いテーブル名の場合xmlの利用が必要となる。(最大31文字まで)

参考サイト