0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

JUnitやバッチテストで便利!

Last updated at Posted at 2025-04-10

✅ 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(*) を実行して存在確認
  • データがあれば DELETEINSERT
  • なければそのまま 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')  

💬 コメント歓迎!

改善アイデアや、「こういうキーでも消したい」といった要望があれば、お気軽にどうぞ!

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?