Agent Grow Tech Notes
🐕‍🦺

JUnit + DBUnitでExcelデータを使ったテストデータ管理術

に公開

はじめに

JUnitを使ったテストを書くとき、データベースのテーブルに必要なデータを毎回用意するのって意外と手間ですよね。
そんなときに便利なのが@Beforeアノテーション。これを使えば、テストが始まる前に自動でテーブルにデータを登録してくれるので、毎回同じ準備を繰り返す必要がなくなります。
ただ、JUnitのテストを書くとき、テストケースごとにまったく違うデータや環境でテストしたいことって結構ありますよね。
そんなときにありがちな落とし穴が「@Beforeでまとめてセットアップすると全部同じになっちゃう」という問題です。

💡補足:JUnit5では@BeforeEachとして提供されています(JUnit4では@Before)。
意味は同じで「各テストの実行前に実行されるメソッド」を定義します。

JUnitの@Before(または@BeforeEach)は便利ですが、テストごとに異なるデータを使いたいときは、テストメソッド内で準備を書く方が安全です。こうするとテストの独立性が上がり、あとで問題が起きたときにも原因を特定しやすくなります。


@Beforeを使うと便利なケース

  • どのテストでも同じデータを使う場合
  • 共通的なモック設定やDIの初期化など、すべてのテストに共通する準備がある場合
  • テスト対象がデータに依存しないロジック中心のとき

@Beforeを使わない方がいいケース

  • テストごとに異なるDB状態や入力データを再現したい場合
  • 各テストで結果を比較したり差異を検証したい場合
  • データの初期化やクレンジングをExcelなど外部ファイルで管理したい場合

こういったケースでは、テストメソッドごとに明示的にセットアップを記述することで、より正確で独立したテストが実現できます。


ExcelデータでDBを初期化する方法

ここでは、DBUnitを使ってExcelファイル(.xlsx)からDBを初期化する方法を紹介します。
これにより、テストデータをファイルで簡単に切り替えられるようになります。

DBUnitを使うメリット

  • Excelでテストデータを可視的に管理できる
  • CLEAN_INSERTによりテーブル初期化を自動化できる
  • 異なるテストケースごとにデータセットを切り替えるだけで再利用可能
  • SQLを直接書かずに済むため、テストの可読性が高い

フォルダ構成(一部抜粋)

batch/
├── src/
│   ├── main/
│   │   ├── java
│   │      ├── AAAAA.java
│   ├── test/
│   │   ├── java
│   │   │  ├── AAAAATest.java
│   │   ├── resources
│   │      ├── TestData_case1.xlsx
│   │      ├── TestData_case2.xlsx

テストコード例(AAAAATest.java)

以下のコードは、Spring Batchのジョブをテストする例です。
ここではJobLauncherTestUtilsを使ってジョブを実行しています。

💬 JobLauncherTestUtilsは、Spring BatchのジョブをJUnitで手軽に起動・検証できる便利なユーティリティクラスです。

AAAAATest.java
import java.io.File;
import java.io.IOException;
import java.sql.Connection;
import java.sql.DriverManager;

import org.dbunit.dataset.IDataSet;
import org.dbunit.dataset.ReplacementDataSet;
import org.dbunit.dataset.excel.XlsDataSet;
import org.dbunit.operation.DatabaseOperation;
import org.dbunit.database.DatabaseConnection;
import org.dbunit.database.IDatabaseConnection;
import org.junit.jupiter.api.Test;
import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.launch.JobLauncher;
import org.springframework.batch.core.repository.JobRepository;
import org.springframework.batch.test.JobLauncherTestUtils;
import org.springframework.batch.core.ExitStatus;
import org.springframework.batch.core.Job;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import static org.assertj.core.api.Assertions.assertThat;

@SpringBootTest
public class AAAAATest {
  private static final String TEST_DATA_DIR = "./src/test/resources/";
  // DB接続情報(本番では外部ファイルで管理推奨)
  private static final String DB_URL = "jdbc:oracle:thin:@localhost:1521:testdb";
  private static final String DB_USER = "testUser";
  private static final String DB_PASS = "testPassword";

  @Autowired
  @Qualifier("AAAAATest") 
  Job job;
  @Autowired
  JobLauncher jobLauncher;
  @Autowired
  JobRepository jobRepository;

  public JobLauncherTestUtils testUtils() {
    JobLauncherTestUtils jobLauncherTestUtils = new JobLauncherTestUtils();
    jobLauncherTestUtils.setJob(job);
    jobLauncherTestUtils.setJobLauncher(jobLauncher);
    jobLauncherTestUtils.setJobRepository(jobRepository);
    return jobLauncherTestUtils;
  }

  @Test
  void テストケース1() throws Exception {
    setupDatabaseWith("TestData_case1.xlsx");
    JobExecution execution = testUtils().launchJob();
    //実行結果が正常であること
    assertThat(execution.getExitStatus()).isEqualTo(ExitStatus.COMPLETED);
  }

  @Test
  void テストケース2() throws Exception {
    setupDatabaseWith("TestData_case2.xlsx");
    JobExecution execution = testUtils().launchJob();
    //実行結果が正常であること
    assertThat(execution.getExitStatus()).isEqualTo(ExitStatus.COMPLETED);
  }
  // 必要に応じて他のテストケースを追加…

  private void setupDatabaseWith(String fileName) throws Exception {
    try (Connection conn = DriverManager.getConnection(DB_URL, DB_USER, DB_PASS)) {
      IDatabaseConnection dbconn = new DatabaseConnection(conn);
      IDataSet dataSet = createExcelDataSet(TEST_DATA_DIR + fileName);
      DatabaseOperation.CLEAN_INSERT.execute(dbconn, dataSet);
    }
  }

  private IDataSet createExcelDataSet(String filePath) throws IOException {
    try {
      ReplacementDataSet dataSet = new ReplacementDataSet(new XlsDataSet(new File(filePath)));
      dataSet.addReplacementObject("null", null);
      return dataSet;
    } catch (Exception e) {
      throw new RuntimeException("Excelファイルの読み込みに失敗しました: " + filePath, e);
    }
  }
}

まとめ

✅ @Beforeを使うべきとき

  • テストの共通初期化が必要なとき
  • データがどのテストでも同一である場合
  • 共通ロジックのモックやDI設定を簡略化したいとき

🚫 @Beforeを避けるべきとき

  • 各テストケースで異なる入力データを使いたいとき
  • テスト間の依存を完全に断ちたいとき
  • 外部ファイル(Excelなど)でデータを明示的に管理したいとき

💡 おすすめの構成

  • テストデータをExcelで管理(DBUnitで読み込み)
  • 各テストメソッドで必要なデータを個別にロード
  • テスト名とファイル名を対応させて、メンテナンス性を向上

🧭 まとめの一言

@Beforeは便利な反面、テストの独立性を損なう危険性もあります。
そのため、**「何を共通化し、何を独立させるか」**を意識したテスト設計が重要です。
DBUnitとExcelの活用で、再現性と保守性の高いテスト環境を構築しましょう 💪

Agent Grow Tech Notes
Agent Grow Tech Notes

Discussion