Files
Claude 4370ac7fdf 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.
2026-10-08 12:35:04 +00:00

6.1 KiB

data-jpa-testcontainers

Companion module for @DataJpaTest with Testcontainers @ServiceConnection on Spring Boot 4.1 on ankurm.com.

What this is

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 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 real failure you get from the one case that genuinely needs a container. See the versions table and the test table below for exactly which is which.

Versions

Dependency Version Verified against
Spring Boot 4.1.1 spring-boot-dependencies-4.1.1.pom
Testcontainers 2.0.5 same BOM; testcontainers-bom-2.0.5.pom
Flyway 12.4.0 same BOM
Hibernate ORM 7.4.5.Final runtime log, this module's own test run
H2 2.4.240 runtime log, this module's own test run
JDK 25 (Temurin) java -version, this module's own build

Quickstart

mvn test                                                      # 9 tests run; 1 skips cleanly
mvn test -Dtest=ServiceConnectionWithoutGuardFailsToStartTest # the one deliberate failure

Test classes

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
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

Captured output

File Contents
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

Two things this article discusses are backed by reading Spring Boot's and Testcontainers' own class files and import manifests rather than by a run in this sandbox, and are called out as such in the post itself: what a successful real-Postgres run under @ServiceConnection would actually return once a Docker daemon is present (the wiring is verified; the live round trip is not), and Testcontainers' reusable-container feature (.withReuse(true) plus ~/.testcontainers.properties), which has nothing to do with Docker availability but was out of scope for a module built in one sitting.