skip to content

Where does a @TestPropertySource source sit in the Environment's property-source ordering, and does it override application.properties and OS environment variables?

level: seniorimportance: should knowfreq 45%

answer

  1. Environment = ordered list, first match wins
  2. Test source beats application.properties + @PropertySource
  3. Does NOT outrank real -D system props / env vars
  4. Profile files sit below @TestPropertySource
  5. Print getPropertySources() to debug

basics

~20 s

Spring inserts the @TestPropertySource properties as the highest-precedence source among the application's own property sources, so it overrides application.properties. It does not, however, override actual JVM system properties or OS environment variables, which Spring still ranks higher.

solid answer

~40 s

The Environment holds an ordered list of PropertySource objects; when a key exists in several, the first one wins. @TestPropertySource inserts its combined source (locations + inlined properties) with precedence above the application's normal sources — so it beats application.properties, application-<profile>.properties, and @PropertySource. That's the whole point: override config for tests. It does NOT get inserted above the standard system-level sources: actual JVM system properties (-Dfoo=bar / System.getProperties()) and OS environment variables retain their higher precedence in the standard ordering. So a value you inline can still be shadowed by a real -D system property of the same name. Practically, for overriding application property files you rely on @TestPropertySource; for overriding something set via -D you'd need to unset it or use @DynamicPropertySource-style registration.

code

java · 13 lines
java
@SpringBootTest
@ActiveProfiles("test")               // loads application-test.properties (app.mode=ci)
@TestPropertySource(properties = "app.mode=unit")
class PrecedenceTest {

    @Autowired ConfigurableEnvironment env;

    @Test
    void inlinedBeatsProfileFile() {
        // @TestPropertySource source ranks above application-test.properties
        assertThat(env.getProperty("app.mode")).isEqualTo("unit");
    }
}

go deeper

for a junior

Know it overrides application.properties.

for a middle

Know it also overrides profile-specific files and @PropertySource.

for a senior

Articulate the full ordered list and that real system/env sources still outrank it.

for a principal

Anticipate CI env-var shadowing and choose the right override mechanism accordingly.

## The Environment as an ordered list Spring's `Environment` exposes a `MutablePropertySources` — an **ordered** list of `PropertySource` objects. `Environment.getProperty("k")` walks the list **front to back** and returns the value from the **first** source that has the key. Precedence is therefore purely about **position** in that list. ## Standard ordering (Boot, roughly high to low) 1. Devtools / test-specific sources when present. 2. **`@TestPropertySource` inlined properties**, then **`@TestPropertySource` locations** (a dedicated, high-priority test source). 3. JVM **system properties** (`System.getProperties()`, i.e. `-Dkey=value`). 4. OS **environment variables**. 5. `application-<profile>.properties/yml` (profile-specific). 6. `application.properties/yml` (default). 7. `@PropertySource` on `@Configuration` classes. The exact registration is done by the TestContext Framework, which **adds** the test property source(s) near the top of the list. ## What @TestPropertySource beats It reliably overrides **application-level** files: `application.properties`, profile-specific variants, and `@PropertySource`. This is its intended job — supply test overrides that win over the app's baked-in config. ## What it does NOT beat In the standard ordering, **actual JVM system properties and OS environment variables sit above the application files**. Spring's TestContext test source is placed to override the **application** sources, but real `-D` system properties still take their normal high precedence. So: ``` @TestPropertySource(properties = "server.port=8081") // but the JVM was launched with -Dserver.port=9090 ``` can leave the effective value at `9090`, because the system-property source ranks above what you're overriding when that key was set as a genuine `-D`. The safe mental model: `@TestPropertySource` wins over **files**, not over genuinely-set OS/JVM sources. ## Why the ordering matters in practice - CI often sets env vars (`SERVER_PORT`, `SPRING_DATASOURCE_URL`); those can silently win over your inlined test values. - If you must force a value regardless, don't rely on inlining a key already provided via `-D`/env; instead control the launch, or register it programmatically. ## Inspecting the order You can inject `ConfigurableEnvironment` and print `getPropertySources()` to see the actual, resolved order for a given test — useful when a value isn't what you expect. ## Relationship to @ActiveProfiles `@ActiveProfiles` decides **which** profile files load (and which `@Profile` beans exist); `@TestPropertySource` decides **what values** land on top. Profile-specific files still sit **below** the `@TestPropertySource` source, so an inlined value beats `application-test.properties` even when the `test` profile is active. ## Gotchas - 'Overrides application.properties' is true; 'overrides everything' is false. - Order among multiple `locations`: later files override earlier ones. - Inlined `properties` override `locations` within the annotation.

  • Your inlined server.port is ignored on CI. Why might that be?
    CI likely exports SERVER_PORT as an OS env var (or passes -Dserver.port). Those system-level sources outrank the application files that @TestPropertySource is placed above, so the env value wins.
  • Does @TestPropertySource override a value set with @ActiveProfiles' profile file?
    Yes — the test property source ranks above application-<profile>.properties, so an inlined key overrides the same key from the profile-specific file.

saying these in an interview costs you the question

  • Claiming @TestPropertySource overrides OS env vars and -D system properties
  • Thinking precedence is about which annotation is 'newer' rather than position in the property-source list
  • Believing profile files override @TestPropertySource

context