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で手軽に起動・検証できる便利なユーティリティクラスです。
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の活用で、再現性と保守性の高いテスト環境を構築しましょう 💪
Discussion