skip to content

How does `@TestPropertySource` work, and how do its `locations` and inlined `properties` relate to `@SpringBootTest(properties=...)`?

level: middleimportance: should knowfreq 45%

answer

  1. locations = files, properties = inlined
  2. inlined beats locations
  3. @SpringBootTest(properties) == inlined @TestPropertySource
  4. no locations attr on @SpringBootTest
  5. locations can't load YAML

basics

~10 s

@TestPropertySource adds test-only property sources: locations loads .properties files, and properties inlines key=value pairs. Inlined properties beat locations, and both override application.properties. @SpringBootTest(properties=...) is essentially the same inlined mechanism.

solid answer

~40 s

`@TestPropertySource` is a Spring TestContext annotation that contributes property sources to a test's `Environment`. `locations = {"classpath:test.properties"}` loads resource files; `properties = {"a=1"}` inlines pairs. When both are present, **inlined `properties` take precedence over `locations`**, and the whole `@TestPropertySource` source outranks `application.properties`, system properties, and OS env. `@SpringBootTest(properties=...)` writes into the same inlined-properties channel, so semantically they're equivalent for the inlined case — `@SpringBootTest` just saves you a second annotation. Use `@TestPropertySource(locations=...)` when you want a reusable file of test config; use inlined `properties` (on either annotation) for a handful of one-off overrides. Both are compile-time-only, so for runtime values you still need `@DynamicPropertySource`, which sits above `@TestPropertySource` in precedence.

code

java · 15 lines
java
@SpringBootTest
@TestPropertySource(
    locations = "classpath:test-overrides.properties", // app.retries=1 here
    properties = "app.retries=5"                        // inlined wins -> 5
)
class RetryConfigTest {

    @Value("${app.retries}")
    int retries;

    @Test
    void inlinedBeatsFile() {
        assertThat(retries).isEqualTo(5);
    }
}

go deeper

for a junior

Know locations = files, properties = inlined pairs, both test-scoped.

for a middle

Explain inlined-beats-locations and equivalence to @SpringBootTest(properties).

for a senior

Cover full precedence, YAML limitation, and inheritance/merging semantics.

for a principal

Weigh files vs inlined vs profiles vs dynamic for maintainable, cache-friendly test config.

## What it is `@TestPropertySource` is part of the Spring TestContext Framework (not Boot-specific — it works with plain `@ContextConfiguration` too). It registers **test-scoped property sources** high in the `Environment` so they override application config during a test. ```java @TestPropertySource( locations = {"classpath:test-overrides.properties"}, properties = {"app.retries=5", "app.cache.enabled=false"} ) ``` ## Two channels - **`locations`** — resource paths (`classpath:`, `file:`) to `.properties`/`.xml` property files. If omitted, Spring can detect a default `<TestClass>.properties` next to the test class. - **`properties`** (inlined) — `key=value` strings baked into the annotation. **Internal precedence:** inlined `properties` are added *after* `locations`, so **inlined wins** when a key appears in both. ## Relationship to `@SpringBootTest(properties=...)` Spring Boot's `@SpringBootTest#properties` feeds the **same inlined test-property mechanism**. So these two are equivalent: ```java @SpringBootTest(properties = {"a=1"}) // vs @SpringBootTest @TestPropertySource(properties = {"a=1"}) ``` `@SpringBootTest` merely offers the attribute inline for convenience. If you need `locations` (a file), you still reach for `@TestPropertySource` because `@SpringBootTest` has no `locations` attribute. ## Overall precedence (test slice, highest first) 1. `@DynamicPropertySource` 2. `@TestPropertySource` inlined `properties` (and `@SpringBootTest(properties)`) 3. `@TestPropertySource` `locations` 4. command-line `args` 5. Java system properties / OS environment 6. `application.properties` / `application.yml` ## Gotchas - **Merging & inheritance.** By default a subclass's `@TestPropertySource` *merges* with the superclass's (inheritance is on). `inheritLocations`/`inheritProperties = false` disables that. - **Empty annotation.** A bare `@TestPropertySource` with neither attribute triggers detection of the default `<TestClass>.properties` file; if that file is missing you get an error. - **YAML not supported by `locations`.** `@TestPropertySource(locations=...)` cannot load `.yml` files (no `PropertySourceFactory` for YAML by default). Use `.properties`, a `factory`, or a `@Profile`/`spring.config.import`. - **Cache key.** Like `properties`, `@TestPropertySource` is part of the context-cache key; differing values fork the cached context. - **Compile-time only.** Both channels require static values; runtime values need `@DynamicPropertySource`. ## When to use which - Reusable, larger config set shared across tests → `@TestPropertySource(locations="classpath:...")` or a test profile. - A few targeted overrides → inlined `properties` on either annotation. - Runtime-derived values (containers, random ports) → `@DynamicPropertySource`.

  • If a key is defined both in `locations` and inlined `properties`, which value applies?
    The inlined `properties` value. Inlined properties are registered with higher precedence than the resource files named in `locations`.
  • Can `@TestPropertySource(locations=...)` load an `application-test.yml`?
    Not by default — there's no built-in YAML `PropertySourceFactory`. Use a `.properties` file, supply a custom `factory`, or activate a test profile instead.

saying these in an interview costs you the question

  • Saying `locations` overrides inlined `properties`
  • Thinking `@SpringBootTest` has a `locations` attribute
  • Assuming `@TestPropertySource(locations)` loads YAML
  • Not knowing subclass property sources merge by default

context