Help us understand the problem. What is going on with this article?

JavaでのDBのテストデータ作成はDbSetupが楽

More than 3 years have passed since last update.

Javaでのデータベースのテストデータ作成にはDBUnitがよく使われますが、自分はDbSetupをオススメします。

なぜDBUnitがイマイチなのか

自分も最初はDBUnitを使ってたのですが、以下の理由からしっくり来ませんでした。

DBを使ったテストでは少量のデータを使うことが多い

ホワイトボックステストで大量のデータを使うことはほとんどなく、単一または複数のテーブルに対して、少量のデータを用意するケースがほとんどです。なので、テストごとにファイル(XML or Excel)を用意するのは面倒です。

テストコードとデータが分離している

テストデータを外部ファイルに保存するため、テストコードとテストデータが分離してしまっています。そのため、何をテストしているのかが分かり辛いです。

そこでDbSetup

そこで見つけたのがDbSetupです。DbSetupはテストデータをJavaコードとして書きます。そのため、少量のテストデータが書きやすく、テストコードとテストデータの位置が近いため、何をテストしているのかが分かりやすいです。

DbSetupの使い方

Destinationオブジェクト

まず接続に必要なDestinationオブジェクトですが、これは2通りの方法で書けます。

// 1. javax.sql.DataSourceから構築
Destination dest = new DataSourceDestination(datasource);

// 2. URL, User, Passwordから構築
Destination dest = new DriverManagerDestination(url, user, password);

テストデータの準備

DbSetupのテストデータは、「テスト対象テーブルのクリア」「テストに必要な小さなデータの定義」の2つからなります。

クリアするテーブルの指定

以下のように、テストで使用するテーブルをクリアするための、Operationオブジェクトを作成します。

import static com.ninja_squad.dbsetup.Operations.*;

// deleteAllFromは複数指定も可能(可変長引数)
public static final Operation DELETE_ALL = deleteAllFrom("character");

// truncateを使う場合
public static final Operation TRUNCATE = truncate("character");

// 一部分だけ削除したいときはSQLを直接書く
public static final Operation DELETE_PARTIAL = sql("delete from character where age < 20");

データの定義

以下のように、テストデータを定義します。まずは、最初に列名を定義してからデータを入れる方法です。

import static com.ninja_squad.dbsetup.Operations.*;

public static final Operation INSERT =
  // テーブルの指定
  insertInto("character")

  // 全てのレコードに同じ値をセットする場合
  .withDefaultValue("gender", "女性")

  // 列の定義
  .columns("headgear", "clothing", "shoes")

  // 1つ目のレコード
  .values("ヘッドバンド ホワイト", "わかばイカT", "キャンバス ホワイト")

  // 2つ目のレコード
  .values("フェイスゴーグル", "スクールブレザー", "パワードレッグス")

  // Operationオブジェクトを作成
  .build();

もう一つは、列と値を同時にセットする方法です。こちらの方が少し冗長ですが、列が多い場合はこちらの方が見やすいので、使い分けてください。Mapから作成する方法もあります。

import static com.ninja_squad.dbsetup.Operations.*;

public static final Operation INSERT =
  // テーブルの指定
  insertInto("character")

  // 全てのレコードに同じ値をセットする場合
  .withDefaultValue("gender", "女性")

  // 1行目の作成
  .row().column("headgear", "ヘッドバンド ホワイト")
        .column("clothing", "わかばイカT")
        .column("shoes", "キャンバス ホワイト")
        .end()

  // 2行目の作成
  .row().column("headgear", "フェイスゴーグル")
        .column("clothing", "スクールブレザー")
        .column("shoes", "パワードレッグス")
        .end()

  // Operationオブジェクトの作成
  .build();

Operationをつなげる

バラバラに作成したOperationオブジェクトを実行したい順番に繋げて、1つのOperationオブジェクトにします。Operationはバラバラに作成できて、かつオブジェクトを生成した段階では何も行われないため、複数のクラス間で共有することも容易です。

import static com.ninja_squad.dbsetup.Operations.*;

Operation ops = sequenceOf(DELETE_ALL, INSERT);

そして実行

最後に、DbSetupクラスにDestinationとOperationを渡すことで、実行されます。

DbSetup dbSetup = new DbSetup(dest, ops);
dbSetup.launch();

最後に

DbSetupは他にもいろいろ機能がありますが、APIドキュメントを見れば分かるように、とてもシンプルで、実行も速いです。日本では全く知られてないのがもったいないです。検討してみてはどうでしょうか。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした