Spring Boot
testing
property override
Java
unit testing

Override a property for a single Spring Boot test

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

It is common to need one Spring Boot test class to run with a different configuration value than the rest of the suite. The safest approach is to keep the override local to that test class so the change is obvious, reproducible, and does not leak into unrelated tests.

Use Inline Properties for Simple Static Overrides

For a one-off override, the shortest solution is usually the properties attribute on @SpringBootTest. This keeps the test setup in one place and makes the changed keys visible during review.

java
1import org.junit.jupiter.api.Test;
2import org.springframework.beans.factory.annotation.Autowired;
3import org.springframework.boot.context.properties.EnableConfigurationProperties;
4import org.springframework.boot.context.properties.ConfigurationProperties;
5import org.springframework.boot.test.context.SpringBootTest;
6import org.springframework.stereotype.Service;
7
8import static org.junit.jupiter.api.Assertions.assertEquals;
9
10@SpringBootTest(properties = {
11    "feature.checkout.enabled=false",
12    "app.retry.max=1"
13})
14class CheckoutDisabledTest {
15
16    @Autowired
17    private FeatureService featureService;
18
19    @Test
20    void usesTheOverriddenValues() {
21        assertEquals(false, featureService.isCheckoutEnabled());
22        assertEquals(1, featureService.maxRetries());
23    }
24
25    @Service
26    @EnableConfigurationProperties(AppProperties.class)
27    static class FeatureService {
28        private final AppProperties properties;
29
30        FeatureService(AppProperties properties) {
31            this.properties = properties;
32        }
33
34        boolean isCheckoutEnabled() {
35            return properties.isCheckoutEnabled();
36        }
37
38        int maxRetries() {
39            return properties.getRetryMax();
40        }
41    }
42
43    @ConfigurationProperties(prefix = "app")
44    static class AppProperties {
45        private boolean checkoutEnabled = true;
46        private int retryMax = 3;
47
48        public boolean isCheckoutEnabled() {
49            return checkoutEnabled;
50        }
51
52        public void setCheckoutEnabled(boolean checkoutEnabled) {
53            this.checkoutEnabled = checkoutEnabled;
54        }
55
56        public int getRetryMax() {
57            return retryMax;
58        }
59
60        public void setRetryMax(int retryMax) {
61            this.retryMax = retryMax;
62        }
63    }
64}

Use this when the values are static strings, numbers, or booleans. It is the most readable option for a single test scenario.

Use @TestPropertySource When the Override Set Gets Larger

If a test needs several related properties, @TestPropertySource can be clearer than stuffing everything into one annotation attribute. It also works well when you want to point at a dedicated test property file.

java
1import org.junit.jupiter.api.Test;
2import org.springframework.boot.test.context.SpringBootTest;
3import org.springframework.test.context.TestPropertySource;
4
5@SpringBootTest
6@TestPropertySource(properties = {
7    "payment.provider.mock=true",
8    "payment.timeout.ms=150",
9    "payment.base-url=http://localhost:8089"
10})
11class PaymentPropertiesTest {
12
13    @Test
14    void contextLoadsWithTestSpecificPaymentSettings() {
15    }
16}

This style is useful when the override values form a small configuration story of their own. The tradeoff is that it adds one more annotation, so it is usually not worth it for only one key.

Use @DynamicPropertySource for Runtime Values

Static annotations stop being convenient once a property depends on something created during test startup, such as a Testcontainers port. That is where @DynamicPropertySource fits.

java
1import org.junit.jupiter.api.Test;
2import org.springframework.boot.test.context.SpringBootTest;
3import org.springframework.test.context.DynamicPropertyRegistry;
4import org.springframework.test.context.DynamicPropertySource;
5
6@SpringBootTest
7class DynamicUrlTest {
8
9    private static final int mockPort = 18081;
10
11    @DynamicPropertySource
12    static void overrideProperties(DynamicPropertyRegistry registry) {
13        registry.add("external.api.base-url", () -> "http://localhost:" + mockPort);
14    }
15
16    @Test
17    void contextLoadsWithDynamicBaseUrl() {
18    }
19}

This keeps runtime-dependent values local to the test class without forcing you to mutate global configuration files.

Verify the Property That Spring Actually Resolved

A property override is not useful if it silently misses the key you intended to change. A small assertion against the Spring Environment or a bound configuration object can save time.

java
1import org.junit.jupiter.api.Test;
2import org.springframework.beans.factory.annotation.Autowired;
3import org.springframework.boot.test.context.SpringBootTest;
4import org.springframework.core.env.Environment;
5
6import static org.junit.jupiter.api.Assertions.assertEquals;
7
8@SpringBootTest(properties = "feature.checkout.enabled=false")
9class PropertyAssertionTest {
10
11    @Autowired
12    private Environment environment;
13
14    @Test
15    void confirmsTheEffectiveValue() {
16        assertEquals("false", environment.getProperty("feature.checkout.enabled"));
17    }
18}

That kind of check catches typos, wrong prefixes, and unexpected precedence issues early.

Choose the Smallest Override Mechanism

Spring offers several ways to change configuration in tests, and using the smallest mechanism that fits the problem keeps the suite predictable:

  • use @SpringBootTest(properties = ...) for a few static values
  • use @TestPropertySource when the override set is larger or file-backed
  • use @DynamicPropertySource when values are created at runtime
  • use profiles only when the whole test class needs a coherent alternate environment

If you only need one key changed, editing application-test.yml is usually the wrong move because it changes behavior for every test that loads that profile.

Common Pitfalls

The most common mistake is changing a shared test configuration file for a single scenario and unintentionally affecting dozens of other tests. Another frequent issue is overriding the wrong key because a configuration prefix changed and the test never asserted the resolved value. Teams also mix @ActiveProfiles, inline properties, and @TestPropertySource without understanding precedence, which makes failures hard to explain. Finally, dynamic properties are often used for values that are actually static, which adds complexity without benefit.

Summary

  • Keep property overrides local to the one test class that needs them.
  • Use inline @SpringBootTest(properties = ...) for simple static changes.
  • Reach for @TestPropertySource when the override set is larger or file-based.
  • Use @DynamicPropertySource for runtime-generated values such as container ports.
  • Assert the resolved property value so a typo does not quietly invalidate the test.

Course illustration
Course illustration

All Rights Reserved.