skip to content

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