How do you use @Sql's executionPhase to run cleanup SQL after a test, and what phases are available?
answer
- default = BEFORE_TEST_METHOD
- AFTER_TEST_METHOD for cleanup
- @Sql is @Repeatable -> stack before+after
- class-phases 6.1+ class-level only
- rollback often makes AFTER unnecessary
basics
~10 sAdd a second @Sql with executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD to run teardown SQL after the test. The default phase is BEFORE_TEST_METHOD, so you can stack a before-script and an after-script on the same method.
solid answer
~40 s`executionPhase` controls when a script runs. The default is `Sql.ExecutionPhase.BEFORE_TEST_METHOD`; set `AFTER_TEST_METHOD` for cleanup. Because `@Sql` is repeatable, you commonly stack two on one method: one seeding before, one deleting after. Modern Spring (6.1+) also adds class-level phases `BEFORE_TEST_CLASS` and `AFTER_TEST_CLASS`, which run once per class for class-level `@Sql` — useful for expensive one-time setup. A key subtlety: if the test runs in a rolled-back test-managed transaction, before-scripts share that transaction and are undone anyway, so explicit AFTER cleanup is mostly needed when tests commit (e.g. @Commit, non-transactional tests, or scripts configured to run in their own transaction). Choosing AFTER_TEST_METHOD keeps the schema clean between committing tests without relying on rollback.
code
java · 13 linesimport static org.springframework.test.context.jdbc.Sql.ExecutionPhase.AFTER_TEST_METHOD;
@SpringBootTest
@Commit // commits, so rollback won't clean up for us
class ProductServiceTest {
@Test
@Sql("/seed-products.sql")
@Sql(scripts = "/wipe-products.sql", executionPhase = AFTER_TEST_METHOD)
void seedsBeforeAndCleansAfter() {
// test body sees seeded rows; wipe runs afterwards
}
}go deeper
Knows there is a before and after phase and default is before.
Can stack before/after @Sql and knows the ExecutionPhase enum values.
Explains the transaction-rollback interaction that makes AFTER often redundant and when it isn't.
Designs isolation strategy: rollback vs commit vs ISOLATED scripts, class-scoped fixtures for suite performance.
## The executionPhase attribute `@Sql(executionPhase = ...)` takes a value of the enum `org.springframework.test.context.jdbc.Sql.ExecutionPhase`. Values: - `BEFORE_TEST_METHOD` — **default**; runs before the annotated test method body. - `AFTER_TEST_METHOD` — runs after the test method (teardown/cleanup). - `BEFORE_TEST_CLASS` — runs once, before any test in the class (Spring Framework 6.1+). Only valid on **class-level** `@Sql`. - `AFTER_TEST_CLASS` — runs once, after all tests in the class (6.1+). Class-level only. Using a class-level phase (`BEFORE_TEST_CLASS`/`AFTER_TEST_CLASS`) on a **method-level** `@Sql` is a configuration error and Spring will reject it. ## @Sql is repeatable `@Sql` is a `@Repeatable` annotation, so you can place several on the same element. The canonical pattern is a before-seed plus an after-cleanup: ```java @Sql(scripts = "/seed.sql") // BEFORE (default) @Sql(scripts = "/cleanup.sql", executionPhase = AFTER_TEST_METHOD) @Test void t() { ... } ``` Under the hood repeated `@Sql`s are collected into an `@SqlGroup` (you can also write that group explicitly). ## Interaction with transactions By default a `@Transactional` test method (which `@DataJpaTest`/`@JdbcTest` apply automatically) runs in a **test-managed transaction that is rolled back** at the end. In that mode: - A `BEFORE_TEST_METHOD` script runs inside that same transaction (by default `SqlConfig.transactionMode = INFERRED`, which uses the existing transaction) and is therefore rolled back with the test. - So explicit `AFTER_TEST_METHOD` cleanup is often **unnecessary** for rolled-back tests. You genuinely need `AFTER_TEST_METHOD` when: - The test **commits** (`@Commit`, `@Rollback(false)`, or a non-transactional test), - Or the script is configured with `@SqlConfig(transactionMode = ISOLATED)` so it commits in its own transaction independent of the test rollback. ## Gotchas - Static imports: people write `executionPhase = AFTER_TEST_METHOD` after `import static ...Sql.ExecutionPhase.AFTER_TEST_METHOD;`. - Class-level `BEFORE_TEST_CLASS` runs before the ApplicationContext-per-class test lifecycle's methods but requires the class-level placement; it's ideal for one-time reference data. - Ordering among multiple same-phase `@Sql`s follows annotation declaration order. ## When to use Prefer rollback-based isolation for speed; reach for `AFTER_TEST_METHOD` cleanup when tests must commit or when you use `ISOLATED` script transactions. Use `BEFORE_TEST_CLASS` for expensive shared fixtures that every test in the class reads but none mutates.
- Why might you NOT need an AFTER_TEST_METHOD cleanup script?Because a default @Transactional test rolls back its transaction, and BEFORE scripts run inside that transaction (INFERRED mode), so their inserts are undone automatically.
- Can you put executionPhase = BEFORE_TEST_CLASS on a method-level @Sql?No. Class-level phases are only valid on class-level @Sql; Spring rejects them on method-level annotations.
saying these in an interview costs you the question
- Claiming AFTER_TEST_METHOD is the default
- Thinking you can only have one @Sql per method
- Assuming BEFORE_TEST_CLASS works on a method-level @Sql