Spring Batchのテスト方法を簡単に解説 | Job全体を起動して確認する
Job全体を起動して確認する
Spring Batchを使うと、CSV取込、データ移行、夜間バッチ、集計処理などの定期処理をJavaで実装できます。
ただ、Spring Batchの処理は最初は「どうやってテストすればいいのか」が分かりづらいです。
この記事では、Spring Batchの簡単なJobを作成し、そのJobをテストコードから起動して、正常終了することを確認する方法を紹介します。
今回確認する内容は次の通りです。
- Spring Batchに必要な依存関係
- 簡単なJobとStepの実装
- Repositoryを呼び出すBatch処理
@SpringBatchTestを使ったテストJobLauncherTestUtilsを使ったJob全体の起動ExitStatus.COMPLETEDによる正常終了の確認
Spring Batchのテストで確認すること
Spring Batchでは、処理全体をJobとして定義します。
そして、Jobの中で実行する1つ1つの処理をStepとして定義します。
Spring BatchのテストではテストコードからJobを起動します。
そして、Jobの実行結果であるJobExecutionを受け取り、正常終了したかどうかを確認します。
今回のテストでは、Jobの終了状態を表すExitStatusを確認します。
assertThat(jobExecution.getExitStatus())
.isEqualTo(ExitStatus.COMPLETED);
ExitStatus.COMPLETEDになっていれば、Jobが最後まで正常に完了したと判断できます。
依存関係を追加する
まず、Spring Batchを使うためにbuild.gradleへ依存関係を追加します。
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-batch'
implementation 'org.springframework.boot:spring-boot-starter-jdbc'
runtimeOnly 'com.h2database:h2'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'org.springframework.batch:spring-batch-test'
}
それぞれの役割は次の通りです。
spring-boot-starter-batch:Spring BootでSpring Batchを使うための依存関係spring-boot-starter-jdbc:DB登録処理でJdbcTemplateを使うための依存関係h2:テスト用のインメモリDBspring-boot-starter-test:JUnitやAssertJなど、テストに必要な機能spring-batch-test:Spring Batchのテスト用ライブラリ
注意点として、spring-batch-testはSpring Boot側ではなくSpring Batch側のライブラリです。
そのため、依存関係の書き方は次のようになります。
testImplementation 'org.springframework.batch:spring-batch-test'
テスト用の設定ファイルを作成する
次に、src/test/resources/application-test.ymlを作成します。
spring:
datasource:
url: jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1
driver-class-name: org.h2.Driver
username: sa
password:
batch:
jdbc:
initialize-schema: always
spring.batch.jdbc.initialize-schema=alwaysを指定することで、Spring Batchが内部で使うメタテーブルをテスト実行時に作成できます。
Spring Batchは、Jobの実行状態やStepの実行状態を管理するために、内部的にメタテーブルを使用します。 H2のようなインメモリDBでテストする場合でも、このテーブルが必要になります。
今回作るBatch処理
今回は、サンプルとして次のような簡単なBatch処理を作ります。
orderImportJobを起動する
↓
orderImportStepが実行される
↓
OrderRepositoryのsaveメソッドを呼び出す
↓
ORDER_INFOテーブルにデータを登録する
複雑なCSV取込やファイル出力ではなく、まずはSpring Batchのテスト方法を理解するために、Repositoryを1回呼び出すだけのシンプルな構成にします。
テスト用のテーブルを作成する
今回は、Batch処理の中でORDER_INFOテーブルにデータを登録します。
そのため、src/test/resources/schema.sqlを作成します。
CREATE TABLE ORDER_INFO (
ID BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
ITEM_NAME VARCHAR(255) NOT NULL,
QUANTITY INT NOT NULL
);
このテーブルに、Batch処理から商品名と数量を登録します。
Repositoryを作成する
次に、DBへ登録するためのRepositoryを作成します。
@Repository
public class OrderRepository {
private final JdbcTemplate jdbcTemplate;
public OrderRepository(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
public void save(String itemName, int quantity) {
jdbcTemplate.update(
"INSERT INTO ORDER_INFO (ITEM_NAME, QUANTITY) VALUES (?, ?)",
itemName,
quantity
);
}
public int count() {
Integer count = jdbcTemplate.queryForObject(
"SELECT COUNT(*) FROM ORDER_INFO",
Integer.class
);
return count == null ? 0 : count;
}
public void deleteAll() {
jdbcTemplate.update("DELETE FROM ORDER_INFO");
}
}
saveメソッドでは、ORDER_INFOテーブルにデータを登録しています。
countメソッドは、テストで登録件数を確認するために用意しています。
deleteAllメソッドは、テスト実行前にテーブルを空にするために使います。
Spring BatchのJobとStepを実装する
次に、Spring BatchのJobとStepを定義します。
@Configuration
public class OrderImportJobConfig {
@Bean
Job orderImportJob(
JobRepository jobRepository,
Step orderImportStep
) {
return new JobBuilder("orderImportJob", jobRepository)
.start(orderImportStep)
.build();
}
@Bean
Step orderImportStep(
JobRepository jobRepository,
PlatformTransactionManager transactionManager,
OrderRepository orderRepository
) {
return new StepBuilder("orderImportStep", jobRepository)
.tasklet((contribution, chunkContext) -> {
orderRepository.save("ノートPC", 2);
return RepeatStatus.FINISHED;
}, transactionManager)
.build();
}
}
このクラスには@Configurationを付けています。
@Configurationを付けることで、このクラスがSpringに設定クラスとして読み込まれます。
orderImportJobメソッドでは、Jobを定義しています。
new JobBuilder("orderImportJob", jobRepository)
.start(orderImportStep)
.build();
ここでは、orderImportJobという名前のJobを作成し、起動時にorderImportStepを実行するようにしています。
次に、orderImportStepメソッドではStepを定義しています。
new StepBuilder("orderImportStep", jobRepository)
.tasklet((contribution, chunkContext) -> {
orderRepository.save("ノートPC", 2);
return RepeatStatus.FINISHED;
}, transactionManager)
.build();
taskletの中に、Stepで実行したい処理を書きます。
今回は、OrderRepositoryのsaveメソッドを呼び出して、DBにデータを登録しています。
最後にRepeatStatus.FINISHEDを返すことで、このStepの処理が完了したことを表します。
Job全体をテストする
では、実際にJob全体を起動するテストコードを書いていきます。
@SpringBatchTest
@SpringBootTest(properties = "spring.batch.job.enabled=false")
@ActiveProfiles("test")
class OrderImportJobTest {
@Autowired
private JobLauncherTestUtils jobLauncherTestUtils;
@Autowired
private OrderRepository orderRepository;
@Test
void job全体を起動して正常終了しデータが登録されること() throws Exception {
orderRepository.deleteAll();
JobParameters jobParameters = new JobParametersBuilder()
.addLong("run.id", System.currentTimeMillis())
.toJobParameters();
JobExecution jobExecution = jobLauncherTestUtils.launchJob(jobParameters);
assertThat(jobExecution.getExitStatus())
.isEqualTo(ExitStatus.COMPLETED);
assertThat(orderRepository.count())
.isEqualTo(1);
}
}
このテストでは、次の流れでJobを確認しています。
テーブルを空にする
↓
JobParametersを作成する
↓
JobLauncherTestUtilsでJobを起動する
↓
JobExecutionを受け取る
↓
ExitStatusがCOMPLETEDであることを確認する
↓
DBに1件登録されていることを確認する
@SpringBatchTestとは
@SpringBatchTestは、Spring Batchのテスト用機能を使うためのアノテーションです。
このアノテーションを付けることで、テストコードからJobやStepを起動するための補助クラスを使えるようになります。
今回使っているJobLauncherTestUtilsも、Spring BatchのテストでJobを起動するためのクラスです。
@Autowired
private JobLauncherTestUtils jobLauncherTestUtils;
JobLauncherTestUtilsを使うと、次のようにテストコードからJob全体を起動できます。
JobExecution jobExecution = jobLauncherTestUtils.launchJob(jobParameters);
@SpringBootTestを付ける理由
今回のテストでは@SpringBootTestも付けています。
@SpringBootTest(properties = "spring.batch.job.enabled=false")
@SpringBootTestを付けることで、Springに登録されているBeanを読み込んだ状態でテストできます。
今回のBatch処理では、次のようなBeanを使っています。
- Job
- Step
- OrderRepository
- JdbcTemplate
- JobRepository
- PlatformTransactionManager
これらをSpringに読み込ませた状態でJobを起動したいため、@SpringBootTestを使っています。
spring.batch.job.enabled=falseを指定する理由
テストコードでは、次のように指定しています。
@SpringBootTest(properties = "spring.batch.job.enabled=false")
Spring BootでSpring BatchのJobが登録されている場合、アプリケーション起動時にJobが自動実行されることがあります。
ただし、今回のテストでやりたいのは、Spring Bootが起動したタイミングでJobを動かすことではありません。 テストメソッドの中で、次のコードを呼び出したタイミングでJobを起動したいです。
jobLauncherTestUtils.launchJob(jobParameters);
そのため、spring.batch.job.enabled=falseを指定して、起動時の自動実行を止めています。
これを指定しないと、テスト開始時にJobが先に動いてしまい、テストメソッド内で起動したJobと合わせて二重実行のような状態になる可能性があります。
JobParametersを指定する理由
Jobを起動するときには、JobParametersを渡しています。
JobParameters jobParameters = new JobParametersBuilder()
.addLong("run.id", System.currentTimeMillis())
.toJobParameters();
Spring Batchでは、同じJobを同じJobParametersで実行すると、同じJob実行として扱われます。
そのため、一度正常終了したJobを同じパラメータでもう一度実行しようとすると、すでに完了済みとして扱われることがあります。
テストは何度も実行することが多いため、毎回異なるJobParametersを渡した方が扱いやすいです。
今回は、run.idに現在時刻を入れることで、テスト実行ごとに別のJob実行として扱えるようにしています。
.addLong("run.id", System.currentTimeMillis())
ExitStatus.COMPLETEDで正常終了を確認する
Jobを起動すると、戻り値としてJobExecutionが返ってきます。
JobExecution jobExecution = jobLauncherTestUtils.launchJob(jobParameters);
JobExecutionには、Jobの実行結果に関する情報が入っています。
その中からgetExitStatus()を使って終了状態を取得します。
assertThat(jobExecution.getExitStatus())
.isEqualTo(ExitStatus.COMPLETED);
ExitStatus.COMPLETEDであれば、Jobが最後まで正常に完了したことを確認できます。
まずは、この確認がSpring BatchのJobテストの基本になります。
DB登録結果も確認する
Jobが正常終了しただけでは、「本当に期待した処理が行われたか」までは分かりません。
今回のJobでは、Stepの中でDB登録を行っています。 そのため、Jobの正常終了に加えて、DBに1件登録されていることも確認しています。
assertThat(orderRepository.count())
.isEqualTo(1);
このように、Spring Batchのテストでは次の2つを組み合わせると分かりやすいです。
Jobが正常終了したか
処理結果が期待通りか
ExitStatus.COMPLETEDでJobの終了状態を確認し、DBの件数確認で処理結果を確認する、という形です。
複数のJobがある場合の注意点
今回のサンプルでは、テスト対象のJobが1つだけという前提です。
もしアプリケーション内に複数のJobがある場合は、JobLauncherTestUtilsがどのJobを起動すればよいか分からなくなることがあります。
その場合は、テスト対象のJobを明示的に指定します。
@Autowired
private JobLauncherTestUtils jobLauncherTestUtils;
@Autowired
@Qualifier("orderImportJob")
private Job orderImportJob;
@BeforeEach
void setUp() {
jobLauncherTestUtils.setJob(orderImportJob);
}
複数Jobがあるプロジェクトでは、どのJobをテストするのかを明示しておくと安全です。
まとめ
この記事では、Spring BatchのJob全体をテストする方法を紹介しました。
Spring Batchのテストでは、APIテストのようにURLを呼び出すのではなく、テストコードからJobを起動します。
基本の流れは次の通りです。
@SpringBatchTestを付ける
@SpringBootTestでSpringのBeanを読み込む
spring.batch.job.enabled=falseで起動時の自動実行を止める
JobParametersを作成する
JobLauncherTestUtilsでJobを起動する
JobExecutionのExitStatusを確認する
必要に応じてDB登録結果も確認する
まずは、ExitStatus.COMPLETEDを確認できれば、Jobが最後まで正常終了したことをテストできます。
さらに、DB登録件数なども確認すると、Batch処理の結果までテストできます。
Spring Batchのテストでは、Job全体を起動して確認する方法を押さえておくと、CSV取込、データ移行、集計処理など、さまざまなBatch処理のテストに応用できます。

コメント
0 件のコメント :
コメントを投稿