✅ JUnitやバッチテストで便利!INSERTだけでテストデータの削除&再登録を自動化
テストコードを書くとき、毎回SQLで DELETE → INSERT
を準備するのって面倒ですよね?
本記事では、**「INSERT文だけ書けば、既存データを削除して再登録してくれる」**便利なJavaユーティリティクラス TestDataLoader
を紹介します。
💡 こんな人におすすめ
- バッチ処理の単体/結合テストを行いたい
- テスト前にデータをきれいに準備しておきたい
- 何度もテストを繰り返してもDBが汚れないようにしたい
🎯 解決したい課題
例えばこんなことをやりたいとします:
-- テスト用データの準備
INSERT INTO users (id, name, email) VALUES (1001, 'Alice', 'alice@example.com');
ですが、すでに id = 1001
のデータが存在すると 重複エラー
が出てしまいます。
そこで...
-- 毎回書くのは面倒…
DELETE FROM users WHERE id = 1001;
INSERT INTO users (id, name, email) VALUES (1001, 'Alice', 'alice@example.com');
このような処理をJava側で自動化してくれるのが TestDataLoader
です!
✅ 使い方
1. SQLファイルを用意(例:test_data.sql
)
-- INSERT文だけでOK(DELETEは不要!)
INSERT INTO users (id, name, email) VALUES (1001, 'Alice', 'alice@example.com');
INSERT INTO users (id, name, email) VALUES ('U0002', 'Bob', 'bob@example.com');
2. Javaコードで呼び出し
Connection conn = DriverManager.getConnection(...);
conn.setAutoCommit(false); // テストなら rollback できるように
TestDataLoader.loadTestData(conn);
conn.commit(); // または conn.rollback();
🧠 実装のポイント(中身)
-
INSERT
文を読み取って、id
カラムを探す -
id
の値でSELECT COUNT(*)
を実行して存在確認 - データがあれば
DELETE
→INSERT
- なければそのまま
INSERT
String checkSql = "SELECT COUNT(*) FROM users WHERE id = 1001";
📦 TestDataLoader.java(コード全文)
package jp.test.util;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.sql.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class TestDataLoader {
/**
* test_data.sql に記載された SQL を読み込み、データ削除および挿入を行う。
* 基本的には INSERT 文を対象とし、ID 重複があれば DELETE → INSERT。
*/
public static void loadTestData(Connection conn) throws Exception {
try (
// ステートメント(SQL実行用)を作成
Statement stmt = conn.createStatement();
// test_data.sql を UTF-8 で読み込み、1行ずつ処理する
BufferedReader reader = new BufferedReader(
new InputStreamReader(
TestDataLoader.class.getClassLoader().getResourceAsStream("test_data.sql"),
"UTF-8"
))
) {
String line;
// SQLファイルを1行ずつ読み込む
while ((line = reader.readLine()) != null) {
line = line.trim(); // 空白削除
// 空行またはコメント行(--で始まる)はスキップ
if (line.isEmpty() || line.startsWith("--")) continue;
// INSERT文の場合
if (line.toUpperCase().startsWith("INSERT")) {
// テーブル名、カラム名、値を抽出
String table = extractTableName(line);
String[] columns = extractColumns(line);
String[] values = extractValues(line);
// パースに失敗したらスキップ
if (table == null || columns == null || values == null) {
System.err.println("解析失敗: " + line);
continue;
}
// "id" カラムの位置を確認
Integer idIndex = findIndex(columns, "id");
if (idIndex != null) {
// idカラムの値を取得
String idValue = values[idIndex].trim();
// 文字列型ならクォートで囲む
if (!idValue.startsWith("'") && !isNumeric(idValue)) {
idValue = "'" + idValue + "'";
}
// 該当IDのレコードが存在するか確認
String selectSql = "SELECT COUNT(*) FROM " + table + " WHERE id = " + idValue;
ResultSet rs = stmt.executeQuery(selectSql);
rs.next();
int count = rs.getInt(1);
rs.close();
// 存在していれば DELETE 実行
if (count > 0) {
String deleteSql = "DELETE FROM " + table + " WHERE id = " + idValue;
stmt.executeUpdate(deleteSql);
System.out.println("削除実行: " + deleteSql);
} else {
System.out.println("データ未存在 → INSERT 実行");
}
} else {
// id カラムが見つからなければ削除はスキップ
System.out.println("'id' カラムが見つからない → 削除スキップ");
}
// 最終的に INSERT を実行
stmt.executeUpdate(line);
System.out.println("挿入成功: " + line);
} else {
// その他の SQL(DELETEやUPDATEなど)も実行可
stmt.executeUpdate(line);
System.out.println("その他SQL実行: " + line);
}
}
}
}
/**
* INSERT文からテーブル名を抽出
*/
private static String extractTableName(String sql) {
Pattern p = Pattern.compile("INSERT\\s+INTO\\s+(\\w+)", Pattern.CASE_INSENSITIVE);
Matcher m = p.matcher(sql);
return m.find() ? m.group(1) : null;
}
/**
* INSERT文からカラム名の配列を抽出
*/
private static String[] extractColumns(String sql) {
int start = sql.indexOf('(');
int end = sql.indexOf(')', start);
if (start < 0 || end < 0) return null;
return sql.substring(start + 1, end).split(",");
}
/**
* INSERT文から VALUES 内の値の配列を抽出
*/
private static String[] extractValues(String sql) {
int start = sql.toUpperCase().indexOf("VALUES");
int open = sql.indexOf('(', start);
int close = sql.indexOf(')', open);
if (start < 0 || open < 0 || close < 0) return null;
return sql.substring(open + 1, close).split(",");
}
/**
* 指定されたカラム名(key)が配列の何番目にあるかを返す
*/
private static Integer findIndex(String[] columns, String key) {
for (int i = 0; i < columns.length; i++) {
if (columns[i].trim().equalsIgnoreCase(key)) {
return i;
}
}
return null;
}
/**
* 数値かどうかを判定(例: '123' → OK)
*/
private static boolean isNumeric(String value) {
try {
Double.parseDouble(value.replace("'", ""));
return true;
} catch (NumberFormatException e) {
return false;
}
}
}
🔧 カスタマイズ案(応用したくなったら)
-
id
以外のキーで存在チェックしたい(例:email
など) - 複合キーに対応したい
-
MERGE INTO
を使った UPSERT にしたい(DB2限定)
📌 まとめ
メリット | 内容 |
---|---|
✅ SQLファイルの準備が楽になる | INSERTだけでOK |
✅ JUnitやバッチテスト前の初期化に使える | @Beforeにも組み込み可 |
✅ 何度テストしてもDBが汚れない | 自動でDELETEしてくれる |
🛠 実行例ログ(JUnitやmainで実行した場合)
✅ DB接続成功
🗑️ 削除実行: DELETE FROM users WHERE id = 1001
✅ 挿入成功: INSERT INTO users (id, name, email) VALUES (1001, 'Alice', 'alice@example.com')
💬 コメント歓迎!
改善アイデアや、「こういうキーでも消したい」といった要望があれば、お気軽にどうぞ!