A restart test must prove that successive executions belong to one persisted JobInstance.
Spring Batch restart tests: assert metadata rows, not repeated ID values
Count the persisted graph
The two-JVM fixture checks one row in BATCH_JOB_INSTANCE and two rows in BATCH_JOB_EXECUTION. It also checks the same instance ID in both process outputs. The row counts matter: two fresh in-memory repositories can each allocate ID 1 and make a superficial equality assertion pass. The JDBC setup is part of the proof.
Check the negative path
After a force-killed worker, a new process cannot launch the same manifest while the repository still says STARTED. The test checks that refusal before operator recovery. Another check changes a source row and confirms the digest guard refuses the restart without adding a JobExecution. These failures reveal more than a final COMPLETED assertion.
Read the business table too
The first process leaves IDs 1 and 2 committed; the recovered process leaves 1 through 4, once each. Keyed output prevents duplicate IDs but does not prove exactly-once broker delivery. Keep an independent test for any remote side effect and the target database's transaction behavior.
Checked excerpt
assertEquals(1, count(databaseUrl, "BATCH_JOB_INSTANCE"));
assertEquals(2, count(databaseUrl, "BATCH_JOB_EXECUTION"));
assertEquals(4, count(databaseUrl, "receipt_import"));Cost and verification
Metadata assertions add a few indexed reads to a slow integration test. They prevent a false positive that can survive status and row-count checks.
Common Mistakes
- Do not compare only generated instance ID 1 across fresh processes.
- Do not test only the successful restart path.
- Do not infer exactly-once remote effects from a unique database key.
Read next
Spring Boot 4 Batch JDBC starter: verify that job history is actually stored, Spring Batch restart across two JVMs after a recorded failed execution, Spring Batch killed worker: fence it, recover STARTED, then restart, Spring tests: separate business rules, wiring and transport.
