skip to content

Persistence & Integration Testing

Testing against real persistence: transactional rollback, SQL scripts, TestEntityManager, Testcontainers, service connections and dynamic properties. Interviewers ask about this because tests that pass on H2 and fail on Postgres are a familiar story.

part ofSpring Frameworkoverview, primer and where to startread it →
on this pageshow

explore

questions

page 1 of 2

What is @DynamicPropertySource in Spring Boot testing, and what problem does it solve?

level: juniorimportance: must knowfreq 62%

answer

  1. static method + DynamicPropertyRegistry
  2. runs before context refresh
  3. supplier/lambda = lazy (container already up)
  4. random Testcontainers port/JDBC URL
  5. manual alternative to @ServiceConnection

basics

~10 s

@DynamicPropertySource marks a static test method that registers property values computed at runtime — like a Testcontainers database's random mapped port or JDBC URL — into Spring's Environment before the application context starts.

solid answer

~40 s

@DynamicPropertySource is a JUnit/Spring test annotation you put on a static method. Inside it you register properties (key + value supplier) into a DynamicPropertyRegistry. Spring calls this method before it refreshes the ApplicationContext, so the values land in the Environment in time for bean creation. It solves the problem that some config values aren't known until runtime: a Testcontainers container exposes a random host port and a generated JDBC URL only after it starts, so you can't hardcode them in application.properties. The method reads the live container values (e.g. container.getJdbcUrl()) and binds them to spring.datasource.url, spring.datasource.username, etc. It's the manual, explicit alternative to @ServiceConnection, which does the same wiring automatically for supported containers.

code

java · 17 lines
java
@SpringBootTest
@Testcontainers
class OrderRepositoryIT {

    @Container
    static final PostgreSQLContainer<?> POSTGRES =
            new PostgreSQLContainer<>("postgres:16");

    @DynamicPropertySource
    static void datasourceProps(DynamicPropertyRegistry registry) {
        registry.add("spring.datasource.url", POSTGRES::getJdbcUrl);
        registry.add("spring.datasource.username", POSTGRES::getUsername);
        registry.add("spring.datasource.password", POSTGRES::getPassword);
    }

    // ... @Autowired repository, @Test methods
}

go deeper

for a junior

Should explain the random-port problem and that a static method registers runtime values into the Environment.

for a middle

Should mention DynamicPropertyRegistry, the supplier/lazy binding, and precedence over property files.

for a senior

Should contrast with @ServiceConnection and explain the before-refresh timing.

for a principal

Should note DynamicPropertyRegistrar and context-cache implications for choosing this pattern.

## The problem Spring reads configuration from the **Environment** — an abstraction over property sources like `application.properties`, environment variables, and system properties. Beans (e.g. a `DataSource`) are configured from these properties when the **ApplicationContext** is refreshed (created and wired) at test startup. Some values are **not known at compile time**. The classic case is **Testcontainers**: a library that boots a real database (Postgres, MySQL, Kafka…) inside a Docker container for integration tests. To avoid port clashes, the container maps its internal port (e.g. Postgres 5432) to a **random free host port**. You only learn that port — and the full JDBC URL — *after* the container starts. You therefore cannot write `spring.datasource.url=...` in a static properties file. ## What @DynamicPropertySource does `@DynamicPropertySource` (from `org.springframework.test.context`) marks a **static** method that receives a `DynamicPropertyRegistry`. You call `registry.add(name, supplier)` to register each property. Spring's `DynamicPropertySourceBeanInitializer`/test context machinery invokes this method and **inserts a property source at the top of the Environment** (highest precedence) **before the context is refreshed**, so the values are available when beans are created. ```java @DynamicPropertySource static void props(DynamicPropertyRegistry registry) { registry.add("spring.datasource.url", POSTGRES::getJdbcUrl); } ``` ### Key mechanics - **Must be `static`.** The method runs before any test instance exists and before the context is built, so it can't be an instance method. - **Value is a `Supplier`, not a plain value.** `registry.add("key", container::getJdbcUrl)` passes a lambda. Spring calls the supplier **lazily**, when the property is actually resolved — this ensures the container has already started. (Typically the container is a `static` field started in a `@BeforeAll` or via `@Container` + `@Testcontainers`, or a static initializer.) - **Highest precedence.** The registered property source overrides `application.properties`/`application-test.properties`, so your test URL wins over any default. ## When to use - Integration tests where a value is computed at runtime: container ports/URLs, a WireMock server's random port, an embedded broker address. - When you need **explicit control** over exactly which properties are set, or the container type isn't covered by `@ServiceConnection`. ## Relationship to @ServiceConnection `@ServiceConnection` (Spring Boot 3.1+) auto-detects a supported container (e.g. `PostgreSQLContainer`) and wires `spring.datasource.*` for you — no method to write. `@DynamicPropertySource` is the **manual alternative**: more boilerplate, but works for any property and any source. Prefer `@ServiceConnection` for supported containers; reach for `@DynamicPropertySource` for custom/unsupported wiring. ## Common gotchas - Forgetting `static` → the method is silently ignored / fails. - Passing `container.getJdbcUrl()` (invoking eagerly) instead of `container::getJdbcUrl` (a supplier) can capture a value before the container is fully started. - Since Spring Framework 6.1 there's also a bean-based alternative, `DynamicPropertyRegistrar`, for cases where you need Spring beans to contribute dynamic properties.

  • Why can't you just put the JDBC URL in application-test.properties?
    Because the URL contains a random host port assigned only after the container starts at runtime; it's unknown at file-authoring time, so it must be registered dynamically.
  • What is the modern one-annotation alternative for supported containers?
    @ServiceConnection (Spring Boot 3.1+), placed on the container field, which auto-wires the connection properties without writing a @DynamicPropertySource method.

saying these in an interview costs you the question

  • Thinking the method can be non-static (instance method).
  • Believing values are read from application.properties rather than registered at runtime.
  • Claiming @DynamicPropertySource starts the container — it only registers properties.

context

open as a page

What is Spring Boot's @ServiceConnection annotation and what problem does it solve in integration tests?

level: juniorimportance: must knowfreq 55%

basics

~20 s

@ServiceConnection is a Spring Boot annotation you put on a Testcontainers container. Boot reads the container's real URL, port, username and password and wires your app's beans (like the DataSource) to it automatically, so you don't set those properties by hand.

open as a page

What is the @Sql annotation in Spring TestContext, and how do you use it to seed data for an integration test?

level: juniorimportance: must knowfreq 70%

basics

~20 s

@Sql runs SQL scripts (or inline statements) against the test's DataSource before a test method. You point it at a .sql file on the classpath, e.g. @Sql("/data.sql"), to insert seed rows before the test runs.

open as a page

What is Testcontainers and why use the @Testcontainers / @Container annotations in a JUnit 5 test?

level: juniorimportance: must knowfreq 70%

basics

~20 s

Testcontainers starts a real service (like PostgreSQL) in a throwaway Docker container for tests. @Testcontainers on the class turns on lifecycle management, and @Container marks a container field so it is started before tests and stopped after.

open as a page

What is TestEntityManager and why would you use it in a @DataJpaTest instead of your repository?

level: juniorimportance: must knowfreq 45%

basics

~20 s

TestEntityManager is a Spring Boot test helper auto-configured by @DataJpaTest. It wraps JPA's EntityManager with test-friendly methods (persist, persistAndFlush, persistFlushFind, find, flush, clear) to set up database rows for a test without going through the repository you're testing.

open as a page

What happens to the database when you annotate a Spring integration test with @Transactional, and why?

level: juniorimportance: must knowfreq 70%

basics

~20 s

Spring starts a transaction before the test method and automatically rolls it back afterward, even if the test passes. So any data written during the test is undone, keeping the database clean and each test isolated.

open as a page

Why must a @DynamicPropertySource method be static, and why do you register a supplier (method reference) rather than a resolved value?

level: middleimportance: must knowfreq 55%

basics

~20 s

It must be static because it runs before any test instance or the context exists. You register a supplier so Spring reads the value lazily — later, once the container has actually started — instead of capturing it too early.

open as a page

What is the difference between a static @Container field and an instance @Container field, and how does it change the container lifecycle?

level: middleimportance: must knowfreq 65%

basics

~20 s

A static @Container field starts once and is shared by all test methods in the class (per-class). An instance (non-static) @Container field is started and stopped fresh for every test method (per-method), giving isolation but running slower.

open as a page

What is the difference between persist, persistAndFlush, and persistFlushFind on TestEntityManager?

level: middleimportance: must knowfreq 40%

basics

~20 s

persist makes the entity managed but sends no SQL yet (INSERT is deferred). persistAndFlush persists and then flushes so the INSERT actually runs. persistFlushFind persists, flushes, then re-reads the row by id and returns that instance, giving you a genuinely loaded entity to assert on.

open as a page

Why must you flush to surface a database constraint violation inside a @DataJpaTest, and how does that interact with the test's transaction?

level: seniorimportance: must knowfreq 35%

basics

~20 s

@DataJpaTest runs each test in a transaction that rolls back and never commits. JPA defers INSERT/UPDATE SQL until flush or commit, so with no commit the DB never checks constraints — unless you flush explicitly. flush() forces the SQL now, so unique/not-null/FK violations throw where you can assert on them.

open as a page

Where do @DynamicPropertySource properties sit in Spring's property precedence, and what are the common gotchas that make them silently not apply?

level: middleimportance: should knowfreq 34%

basics

~10 s

Their property source is added with high precedence, so it overrides application.properties and @TestPropertySource defaults. Common gotchas: forgetting static, evaluating the value eagerly, or the container not being started before the property is read.

open as a page

Which container types does @ServiceConnection support automatically, and how do you use it with a GenericContainer?

level: middleimportance: should knowfreq 40%

basics

~20 s

It works out of the box with specialized Testcontainers classes like PostgreSQLContainer, MySQLContainer, MongoDBContainer, KafkaContainer, RabbitMQContainer, Neo4jContainer, etc. For a plain GenericContainer, Boot can't tell the service type, so you give it a hint: @ServiceConnection(name = "redis").

open as a page

How does @ServiceConnection differ from @DynamicPropertySource, and when might you still need the latter?

level: middleimportance: should knowfreq 45%

basics

~10 s

@DynamicPropertySource manually maps container getters to string property keys. @ServiceConnection does it automatically with a typed ConnectionDetails bean — less code, no typos. You still use @DynamicPropertySource for properties that have no ConnectionDetails mapping.

open as a page

How do you use @Sql's executionPhase to run cleanup SQL after a test, and what phases are available?

level: middleimportance: should knowfreq 55%

basics

~10 s

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

open as a page

How do you make a transactional test commit its changes instead of rolling back, and when would you?

level: middleimportance: should knowfreq 40%

basics

~20 s

Add @Commit (or equivalently @Rollback(false)) on the test method or class. Then Spring commits the transaction instead of rolling it back. Useful rarely — e.g. inspecting persisted data or seeding across tests — but it breaks isolation, so you must clean up yourself.

open as a page

Compare @DynamicPropertySource with @ServiceConnection. When would you choose each?

level: seniorimportance: should knowfreq 48%

basics

~20 s

@ServiceConnection auto-wires connection properties for supported Testcontainers (like PostgreSQLContainer) with zero code. @DynamicPropertySource is the manual way: you write a static method mapping each property yourself. Use @ServiceConnection for supported containers; @DynamicPropertySource for anything custom.

open as a page

How does @ServiceConnection work internally — what turns a @Container into wired beans?

level: seniorimportance: should knowfreq 30%

basics

~10 s

Boot scans the test class for @ServiceConnection fields, builds a ConnectionSource for each container, and uses a matching ConnectionDetailsFactory to create a typed ConnectionDetails bean. Auto-configuration then consumes that bean instead of properties.

open as a page

Explain @SqlGroup and @SqlMergeMode: how do class-level and method-level @Sql scripts combine?

level: seniorimportance: should knowfreq 35%

basics

~10 s

@SqlGroup is the container for multiple @Sql annotations (created automatically since @Sql is repeatable). By default, method-level @Sql OVERRIDES class-level @Sql. @SqlMergeMode(MERGE) changes that so method scripts run in addition to the class-level ones.

open as a page

What does @SqlConfig configure, and when would you set errorMode, separator, or dataSource?

level: seniorimportance: should knowfreq 45%

basics

~10 s

@SqlConfig customizes how @Sql parses and runs scripts: errorMode (fail vs ignore failed statements), separator (statement delimiter, e.g. ;), commentPrefixes, blockCommentStart/End, encoding, dataSource and transactionManager bean names, and transactionMode.

open as a page

How do you connect a Spring Boot application context to a Testcontainers-managed database, and why can't you just hardcode the URL?

level: seniorimportance: should knowfreq 55%

basics

~20 s

Testcontainers maps the container's port to a random host port, so the JDBC URL isn't known until the container starts. You feed it into Spring at runtime with @DynamicPropertySource (or @ServiceConnection in Boot 3.1+), not a hardcoded property.

open as a page

When wrapping an arbitrary image in GenericContainer, how does Testcontainers know the service is actually ready, and what goes wrong if the wait strategy is wrong?

level: seniorimportance: should knowfreq 40%

basics

~20 s

start() blocks until a wait strategy says the container is ready. By default it waits for the mapped ports to accept connections. For custom images you often set a better one — like waiting for a log line or an HTTP 200 — otherwise tests hit a service that's up but not yet accepting real requests.

open as a page

When arranging fixtures, why prefer TestEntityManager over repository.save, and what role do flush and clear play with the first-level cache?

level: seniorimportance: should knowfreq 25%

basics

~20 s

Building fixtures with the repository you're testing couples Arrange to the code under test, so a bug can hide. TestEntityManager keeps them independent. After persisting, the entity sits in the first-level cache, so a later findById may return it without a DB read; flush pushes SQL and clear detaches everything, forcing a real reload.

open as a page

What are @BeforeTransaction and @AfterTransaction for, and how do they order relative to @BeforeEach in a transactional test?

level: seniorimportance: should knowfreq 22%

basics

~20 s

@BeforeTransaction runs before the test's managed transaction starts; @AfterTransaction runs after it ends (commit or rollback). They let you set up or verify data outside the transaction — for example seeding rows that must survive rollback, or asserting the final committed state.

open as a page

How do you programmatically control commit/rollback and cross multiple transaction boundaries within a single transactional test method?

level: seniorimportance: should knowfreq 18%

basics

~20 s

Use the static TestTransaction helper. Inside a @Transactional test you can call TestTransaction.flagForCommit() or flagForRollback(), then TestTransaction.end() to finish the current transaction, and TestTransaction.start() to begin a fresh one — letting one test span several transactions.

open as a page

Your integration suite spins up Postgres, Kafka, and Redis containers and has grown slow and occasionally flaky. As a principal engineer, how do you architect Testcontainers usage for speed and reliability?

level: principalimportance: should knowfreq 30%

basics

~20 s

Share containers instead of recreating them: use static or singleton containers reused across all test classes, wire stable connection details so Spring's context cache is reused, reset data between tests instead of restarting containers, set correct wait strategies to kill flakiness, and consider reuse/Testcontainers Cloud in CI.

open as a page

As a principal engineer, how do you decide when TestEntityManager + @DataJpaTest is the right test tool versus a fuller integration test, and what fidelity limits should you call out to the team?

level: principalimportance: should knowfreq 18%

basics

~20 s

Use @DataJpaTest with TestEntityManager for fast, isolated persistence-layer tests: entity mappings, custom queries, and constraint behavior arranged with persistFlushFind and asserted after flush. Move to Testcontainers or @SpringBootTest when database fidelity (dialect, deferred constraints, real transactions) or cross-layer behavior matters, because the default H2 + rollback-only slice can't prove those.

open as a page

Why can @Transactional rollback-per-test integration tests give false confidence, and how do you mitigate it?

level: principalimportance: should knowfreq 30%

basics

~20 s

Because the whole test runs in one never-committed transaction, save-then-find can be served from the JPA first-level cache without real SQL, flush/constraint errors may never surface, and lazy loading always works. Mitigate by flushing and clearing the persistence context, and testing commit paths.

open as a page

Besides @Container test fields, how can @ServiceConnection be used with a container @Bean, and why does that matter for development-time?

level: seniorimportance: nice to knowfreq 25%

basics

~20 s

You can define the container as a @Bean in a @TestConfiguration and annotate that bean with @ServiceConnection. Spring Boot manages its lifecycle. You can import the same config into a local main() to run the app against real containers during development.

open as a page

What is TransactionalTestExecutionListener, and how does it decide which transaction manager to use and whether to commit or roll back?

level: seniorimportance: nice to knowfreq 12%

basics

~20 s

It's one of Spring's built-in TestExecutionListeners that provides transactional test behavior: it starts a transaction before a @Transactional test method and rolls it back (or commits) after. It picks the PlatformTransactionManager from the context and reads @Rollback/@Commit to decide the outcome.

open as a page

How does @DynamicPropertySource interact with Spring's TestContext cache, and what is DynamicPropertyRegistrar? What pitfalls should a principal engineer watch for?

level: principalimportance: nice to knowfreq 26%

basics

~20 s

Spring caches loaded contexts across test classes to speed suites. Using a shared singleton container plus consistent @DynamicPropertySource keeps the cache key stable so the context is reused. DynamicPropertyRegistrar (Spring 6.1+) lets Spring beans, not just static test methods, contribute dynamic properties.

open as a page

showing 1–30 of 31