Correct post #45: Flyway and Liquibase DO run under @DataJpaTest by default

The previous commit's FlywayDoesNotRunUnderPlainDataJpaTestTest and
FlywayRunsWhenExplicitlyImportedTest rested on a false premise: that none of
@DataJpaTest's four documented meta-annotations import FlywayAutoConfiguration,
so migrations never run under the slice. That reading of the four .imports
manifests is accurate but incomplete. @AutoConfigureJdbc is itself
meta-annotated with @AutoConfigureDataSourceInitialization (new in Boot 4.0),
and Spring's ImportAutoConfigurationImportSelector walks the full
meta-annotation tree transitively. That fifth annotation's own .imports
resource is loaded via ClassLoader.getResources(), which merges same-named
files from every jar on the classpath -- spring-boot-jdbc-test,
spring-boot-flyway and spring-boot-liquibase each ship one at the identical
path, contributing DataSourceInitializationAutoConfiguration,
FlywayAutoConfiguration and LiquibaseAutoConfiguration respectively.

Replaced the two wrong-premise test classes with FlywayRunsByDefaultTest
(proves the bean is present and the migration actually ran) and
ExplicitFlywayImportIsRedundantTest (proves the old "add the import yourself"
advice is harmless but unnecessary on Boot 4.1). Added javap/unzip evidence
for the new mechanism to docs/output, re-captured the full test run (9 tests,
1 clean skip), corrected README's test table, and closed a second,
unrelated gap: the unzip -l listing quoted in the @ServiceConnection section
had no matching captured transcript -- it does now.
This commit is contained in:
Claude
2026-10-08 12:35:04 +00:00
parent 97f946136b
commit 4370ac7fdf
9 changed files with 298 additions and 188 deletions
+18 -7
View File
@@ -6,9 +6,19 @@ Companion module for [`@DataJpaTest` with Testcontainers `@ServiceConnection` on
Six JUnit 5 test classes against one JPA repository (`Product`), each isolating one fact about
what `@DataJpaTest`'s default embedded-database substitution actually does, what
`@ServiceConnection` wires in its place, and what the defaults do not do (Flyway migrations are
not one of them). No `docs/NN-topic.md` chapters — everything beyond what fits in the post itself
is in the Javadoc on these six test classes and in the captured transcripts below.
`@ServiceConnection` wires in its place, and what the defaults do run that you might not expect
(Flyway migrations, as it turns out, are one of them). No `docs/NN-topic.md` chapters —
everything beyond what fits in the post itself is in the Javadoc on these six test classes and in
the captured transcripts below.
**Correction, made before this module was published:** an earlier draft of two of these classes
asserted that `@DataJpaTest` never runs Flyway or Liquibase migrations, on the strength of reading
its four documented meta-annotations' `.imports` manifests and finding no mention of either tool.
That reading was accurate and incomplete — see `FlywayRunsByDefaultTest`'s Javadoc and
`docs/output/06-autoconfigurejdbc-meta-annotation-javap.txt` for the fifth, undocumented-for-this-slice
meta-annotation that actually wires them in. The two test classes below that carried the wrong
premise (`FlywayDoesNotRunUnderPlainDataJpaTestTest`, `FlywayRunsWhenExplicitlyImportedTest`) were
replaced, not patched, with `FlywayRunsByDefaultTest` and `ExplicitFlywayImportIsRedundantTest`.
**This module was built and tested in a sandbox with no Docker daemon available.** Five of the six
test classes run and pass (or cleanly skip) without one. The sixth demonstrates, on purpose, the
@@ -29,7 +39,7 @@ the test table below for exactly which is which.
## Quickstart
```bash
mvn test # 6 of 7 run; 1 skips cleanly
mvn test # 9 tests run; 1 skips cleanly
mvn test -Dtest=ServiceConnectionWithoutGuardFailsToStartTest # the one deliberate failure
```
@@ -38,8 +48,8 @@ mvn test -Dtest=ServiceConnectionWithoutGuardFailsToStartTest # the one delibera
| Class | What it proves | Needs Docker | Outcome here |
|---|---|---|---|
| `DefaultReplacementTest` | Plain `@DataJpaTest` substitutes an embedded H2 DataSource, and each test method is rolled back independently of execution order | No | Pass |
| `FlywayDoesNotRunUnderPlainDataJpaTestTest` | `@DataJpaTest`'s own auto-configuration import list never includes `FlywayAutoConfiguration` or `LiquibaseAutoConfiguration` — your migrations never run, substitution or not | No | Pass |
| `FlywayRunsWhenExplicitlyImportedTest` | The one-line fix: `@ImportAutoConfiguration(FlywayAutoConfiguration.class)` | No | Pass |
| `FlywayRunsByDefaultTest` | A plain `@DataJpaTest`, with flyway-core and `spring-boot-starter-flyway` on the (test) classpath, runs the real Flyway migration by default — no `@ImportAutoConfiguration` needed, because `@AutoConfigureJdbc` is itself meta-annotated with `@AutoConfigureDataSourceInitialization`, which merges contributions from every jar on the classpath that ships one | No | Pass |
| `ExplicitFlywayImportIsRedundantTest` | Explicitly re-adding `@ImportAutoConfiguration(FlywayAutoConfiguration.class)` — the old advice for the (incorrect) premise above — changes nothing: still exactly one `flyway` bean, still exactly one applied migration | No | Pass |
| `NoSubstitutionStillSilentlyUsesH2Test` | `@AutoConfigureTestDatabase(replace = Replace.NONE)` alone is not a guarantee of a real connection — with nothing else configured, ordinary `DataSourceAutoConfiguration` still silently hands you embedded H2 | No | Pass |
| `ServiceConnectionSkipsCleanlyTest` | The intended shape: `@ServiceConnection` + `@Container` + `replace = Replace.NONE`, guarded by Testcontainers 2.0's new `@EnabledIfDockerAvailable` | Yes (guarded) | Skipped |
| `ServiceConnectionWithoutGuardFailsToStartTest` | The same class, minus the guard | Yes (unguarded) | **Fails — on purpose.** Excluded from `mvn test` by name in `pom.xml`; run it explicitly |
@@ -48,11 +58,12 @@ mvn test -Dtest=ServiceConnectionWithoutGuardFailsToStartTest # the one delibera
| File | Contents |
|---|---|
| `docs/output/00-full-test-run.txt` | `mvn clean test` — 7 tests, 1 skip, build green |
| `docs/output/00-full-test-run.txt` | `mvn clean test` — 9 tests, 1 skip, build green |
| `docs/output/01-postgresqlcontainer-relocation-javap.txt` | `javap` proof that `org.testcontainers.postgresql.PostgreSQLContainer` (2.0.5's new home) and the deprecated `org.testcontainers.containers.PostgreSQLContainer` shim live in the *same* jar, and that the old class alone keeps the self-referential `<SELF extends ...>` generic |
| `docs/output/02-serviceconnection-unaffected-javap.txt` | `javap` proof that `JdbcContainerConnectionDetailsFactory` is typed against `org.testcontainers.containers.JdbcDatabaseContainer<?>` — unmoved by the 2.0 package split, which is why `@ServiceConnection` needed no changes |
| `docs/output/04-service-connection-without-guard-fails.txt` | The real `ContainerFetchException` / `IllegalStateException` from trying to start a Postgres container with no Docker daemon present, and no guard against that |
| `docs/output/05-service-connection-skips-cleanly.txt` | The same absence of Docker, this time behind `@EnabledIfDockerAvailable` — one line, `Skipped: 1`, no failure |
| `docs/output/06-autoconfigurejdbc-meta-annotation-javap.txt` | `javap` proof that `AutoConfigureJdbc.class` is meta-annotated with `@AutoConfigureDataSourceInitialization`, plus the matching `META-INF/spring/...AutoConfigureDataSourceInitialization.imports` file contents from both `spring-boot-jdbc-test` and `spring-boot-flyway` — the actual mechanism behind `FlywayRunsByDefaultTest` |
## What's sourced from code, not run here