or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

assertj-integration.mdcontext-runners.mdindex.mdintegration-testing.mdjson-testing.mdoutput-capture.mdtest-configuration.mdtest-properties.mdweb-test-utilities.md
tile.json

test-properties.mddocs/

Test Property Management

Utilities for managing test-specific properties, environment variables, and system properties during test execution.

Package

org.springframework.boot.test.util

Core Imports

import org.springframework.boot.test.util.TestPropertyValues;
import org.springframework.boot.test.util.TestPropertyValues.Type;
import org.springframework.boot.test.util.ApplicationContextTestUtils;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.env.ConfigurableEnvironment;

TestPropertyValues

Full Package: org.springframework.boot.test.util.TestPropertyValues

Fluent API for creating and applying test property values.

public final class TestPropertyValues {

    // Factory methods - all accept varargs, can be empty
    public static TestPropertyValues of(String... pairs);          // "key=value"
    public static TestPropertyValues of(Map<String, String> map);
    public static TestPropertyValues of(Iterable<String> pairs);
    public static TestPropertyValues of(Stream<String> stream);
    public static <T> TestPropertyValues of(Stream<T> stream, Function<T, Pair> mapper); // @since 2.4.0
    public static TestPropertyValues empty();

    // Builder methods - chainable
    public TestPropertyValues and(String... pairs);
    public TestPropertyValues and(Iterable<String> pairs); // @since 2.4.0
    public TestPropertyValues and(Stream<String> stream); // @since 2.4.0
    public TestPropertyValues and(Map<String, String> map); // @since 2.4.0
    public <T> TestPropertyValues and(Stream<T> stream, Function<T, Pair> mapper); // @since 2.4.0

    // Application methods - cannot be null
    public void applyTo(ConfigurableApplicationContext context);
    public void applyTo(ConfigurableEnvironment environment);
    public void applyTo(ConfigurableEnvironment environment, Type type);
    public void applyTo(ConfigurableEnvironment environment, Type type, String name);

    // System properties - temporary, auto-removed after action
    public void applyToSystemProperties(Runnable action); // @since 3.0.0
    public <T> T applyToSystemProperties(Callable<T> call);

    enum Type {
        SYSTEM_ENVIRONMENT,  // System environment style
        MAP;                 // Map style (default)

        /**
         * Get the property source class for this type
         * @return the property source class
         */
        public Class<? extends MapPropertySource> getSourceClass();
    }

    /**
     * Pair of property name and value
     * @since 2.4.0
     */
    public static final class Pair {
        /**
         * Add this pair to a map
         * @param properties the properties map to add to
         */
        public void addTo(Map<String, @Nullable Object> properties);

        /**
         * Parse a string into a Pair
         * @param pair the "key=value" string
         * @return the Pair
         */
        public static Pair parse(String pair);

        /**
         * Create a Pair from a map entry
         * @param entry the map entry
         * @return the Pair
         * @since 2.4.0
         */
        public static Pair fromMapEntry(Map.Entry<String, String> entry);

        /**
         * Create a Pair from key and value
         * @param name the property name
         * @param value the property value
         * @return the Pair
         * @since 2.4.0
         */
        public static Pair of(String name, String value);
    }
}

Common Patterns

Pattern: With Context Runners

import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.boot.test.util.TestPropertyValues;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;

class PropertyTest {

    @Test
    void testWithProperties() {
        new ApplicationContextRunner()
            .run(context -> {
                TestPropertyValues
                    .of("app.name=test", "app.timeout=30")
                    .applyTo(context);
                    
                assertThat(context.getEnvironment().getProperty("app.name"))
                    .isEqualTo("test");
            });
    }
}

Pattern: Temporary System Properties

@Test
void testSystemProperties() {
    TestPropertyValues
        .of("java.io.tmpdir=/tmp/test")
        .applyToSystemProperties(() -> {
            // Property available here
            String tmpDir = System.getProperty("java.io.tmpdir");
            assertThat(tmpDir).isEqualTo("/tmp/test");
        });
    // Property removed here
}

Pattern: Fluent Building

TestPropertyValues props = TestPropertyValues
    .of("db.url=jdbc:h2:mem:test")
    .and("db.username=sa")
    .and(Map.of("db.password", ""));

Pattern: From Map

@Test
void testFromMap() {
    Map<String, String> config = Map.of(
        "server.port", "8080",
        "server.address", "localhost",
        "server.compression.enabled", "true"
    );

    new ApplicationContextRunner()
        .run(context -> {
            TestPropertyValues.of(config).applyTo(context);

            assertThat(context.getEnvironment().getProperty("server.port"))
                .isEqualTo("8080");
            assertThat(context.getEnvironment().getProperty("server.compression.enabled"))
                .isEqualTo("true");
        });
}

Pattern: Callable with Return Value

@Test
void testSystemPropertiesWithReturn() {
    Integer port = TestPropertyValues
        .of("test.server.port=9090")
        .applyToSystemProperties(() -> {
            String portStr = System.getProperty("test.server.port");
            return Integer.parseInt(portStr);
        });

    assertThat(port).isEqualTo(9090);
    assertThat(System.getProperty("test.server.port")).isNull();
}

Pattern: Override Existing Properties

@Test
void testOverrideProperties() {
    new ApplicationContextRunner()
        .withPropertyValues("app.mode=production")
        .run(context -> {
            // Original property
            assertThat(context.getEnvironment().getProperty("app.mode"))
                .isEqualTo("production");

            // Override with test value
            TestPropertyValues
                .of("app.mode=test")
                .applyTo(context.getEnvironment());

            assertThat(context.getEnvironment().getProperty("app.mode"))
                .isEqualTo("test");
        });
}

Pattern: Multiple Property Sets

@Test
void testMultiplePropertySets() {
    TestPropertyValues baseProps = TestPropertyValues.of(
        "app.name=TestApp",
        "app.version=1.0.0"
    );

    TestPropertyValues dbProps = TestPropertyValues.of(
        "spring.datasource.url=jdbc:h2:mem:test",
        "spring.datasource.driver-class-name=org.h2.Driver"
    );

    TestPropertyValues cacheProps = TestPropertyValues.of(
        "spring.cache.type=simple",
        "spring.cache.cache-names=users,products"
    );

    new ApplicationContextRunner()
        .run(context -> {
            baseProps.applyTo(context);
            dbProps.applyTo(context);
            cacheProps.applyTo(context);

            assertThat(context.getEnvironment().getProperty("app.name"))
                .isEqualTo("TestApp");
            assertThat(context.getEnvironment().getProperty("spring.datasource.url"))
                .contains("h2:mem:test");
            assertThat(context.getEnvironment().getProperty("spring.cache.type"))
                .isEqualTo("simple");
        });
}

Pattern: Environment-Specific Configuration

@Test
void testEnvironmentSpecificProperties() {
    TestPropertyValues testEnv = TestPropertyValues.of(
        "spring.profiles.active=test",
        "logging.level.root=DEBUG",
        "spring.jpa.show-sql=true"
    );

    new ApplicationContextRunner()
        .withUserConfiguration(AppConfig.class)
        .run(context -> {
            testEnv.applyTo(context);

            assertThat(context.getEnvironment().getActiveProfiles())
                .contains("test");
            assertThat(context.getEnvironment().getProperty("logging.level.root"))
                .isEqualTo("DEBUG");
        });
}

Pattern: Property Type Specification

@Test
void testWithSystemEnvironmentType() {
    new ApplicationContextRunner()
        .run(context -> {
            TestPropertyValues
                .of("MY_ENV_VAR=value")
                .applyTo(
                    context.getEnvironment(),
                    TestPropertyValues.Type.SYSTEM_ENVIRONMENT,
                    "test-env-props"
                );

            assertThat(context.getEnvironment().getProperty("MY_ENV_VAR"))
                .isEqualTo("value");
        });
}

@Test
void testWithMapType() {
    new ApplicationContextRunner()
        .run(context -> {
            TestPropertyValues
                .of("my.property=value")
                .applyTo(
                    context.getEnvironment(),
                    TestPropertyValues.Type.MAP,
                    "test-map-props"
                );

            assertThat(context.getEnvironment().getProperty("my.property"))
                .isEqualTo("value");
        });
}

Pattern: Testing @ConfigurationProperties

@ConfigurationProperties(prefix = "app")
class AppProperties {
    private String name;
    private int timeout;
    private boolean enabled;

    // getters and setters
}

@Configuration
@EnableConfigurationProperties(AppProperties.class)
class AppConfig {}

@Test
void testConfigurationPropertiesBinding() {
    new ApplicationContextRunner()
        .withUserConfiguration(AppConfig.class)
        .run(context -> {
            TestPropertyValues
                .of(
                    "app.name=TestApplication",
                    "app.timeout=5000",
                    "app.enabled=true"
                )
                .applyTo(context.getEnvironment());

            AppProperties props = context.getBean(AppProperties.class);
            assertThat(props.getName()).isEqualTo("TestApplication");
            assertThat(props.getTimeout()).isEqualTo(5000);
            assertThat(props.isEnabled()).isTrue();
        });
}

Pattern: Complex Nested Properties

@Test
void testNestedProperties() {
    TestPropertyValues props = TestPropertyValues.of(
        "server.tomcat.threads.max=200",
        "server.tomcat.threads.min=10",
        "server.tomcat.max-connections=10000",
        "server.tomcat.accept-count=100"
    );

    new ApplicationContextRunner()
        .run(context -> {
            props.applyTo(context);

            assertThat(context.getEnvironment().getProperty("server.tomcat.threads.max"))
                .isEqualTo("200");
            assertThat(context.getEnvironment().getProperty("server.tomcat.max-connections"))
                .isEqualTo("10000");
        });
}

Pattern: List and Array Properties

@Test
void testListProperties() {
    TestPropertyValues props = TestPropertyValues.of(
        "app.allowed-origins[0]=http://localhost:3000",
        "app.allowed-origins[1]=http://localhost:4200",
        "app.allowed-origins[2]=https://example.com"
    );

    new ApplicationContextRunner()
        .run(context -> {
            props.applyTo(context);

            assertThat(context.getEnvironment().getProperty("app.allowed-origins[0]"))
                .isEqualTo("http://localhost:3000");
            assertThat(context.getEnvironment().getProperty("app.allowed-origins[2]"))
                .isEqualTo("https://example.com");
        });
}

Pattern: Boolean and Numeric Properties

@Test
void testTypedProperties() {
    TestPropertyValues props = TestPropertyValues.of(
        "feature.enabled=true",
        "cache.size=1024",
        "retry.max-attempts=3",
        "timeout.seconds=30.5"
    );

    new ApplicationContextRunner()
        .run(context -> {
            props.applyTo(context);

            assertThat(context.getEnvironment().getProperty("feature.enabled", Boolean.class))
                .isTrue();
            assertThat(context.getEnvironment().getProperty("cache.size", Integer.class))
                .isEqualTo(1024);
            assertThat(context.getEnvironment().getProperty("timeout.seconds", Double.class))
                .isEqualTo(30.5);
        });
}

Pattern: Empty Values and Null Handling

@Test
void testEmptyAndNullValues() {
    TestPropertyValues props = TestPropertyValues.of(
        "empty.property=",
        "whitespace.property=   ",
        "normal.property=value"
    );

    new ApplicationContextRunner()
        .run(context -> {
            props.applyTo(context);

            assertThat(context.getEnvironment().getProperty("empty.property"))
                .isEmpty();
            assertThat(context.getEnvironment().getProperty("whitespace.property"))
                .isEqualTo("   ");
            assertThat(context.getEnvironment().getProperty("normal.property"))
                .isEqualTo("value");
        });
}

Pattern: URL and Path Properties

@Test
void testUrlAndPathProperties() {
    TestPropertyValues props = TestPropertyValues.of(
        "app.api.base-url=https://api.example.com/v1",
        "app.file.upload-dir=/tmp/uploads",
        "app.jdbc.url=jdbc:postgresql://localhost:5432/testdb"
    );

    new ApplicationContextRunner()
        .run(context -> {
            props.applyTo(context);

            assertThat(context.getEnvironment().getProperty("app.api.base-url"))
                .startsWith("https://");
            assertThat(context.getEnvironment().getProperty("app.file.upload-dir"))
                .isEqualTo("/tmp/uploads");
        });
}

Pattern: Profile-Specific Properties

@Test
void testProfileSpecificProperties() {
    TestPropertyValues devProps = TestPropertyValues.of(
        "spring.profiles.active=dev",
        "logging.level.root=DEBUG",
        "spring.h2.console.enabled=true"
    );

    TestPropertyValues prodProps = TestPropertyValues.of(
        "spring.profiles.active=prod",
        "logging.level.root=WARN",
        "spring.h2.console.enabled=false"
    );

    // Test dev profile
    new ApplicationContextRunner()
        .run(context -> {
            devProps.applyTo(context);
            assertThat(context.getEnvironment().getProperty("spring.h2.console.enabled"))
                .isEqualTo("true");
        });

    // Test prod profile
    new ApplicationContextRunner()
        .run(context -> {
            prodProps.applyTo(context);
            assertThat(context.getEnvironment().getProperty("spring.h2.console.enabled"))
                .isEqualTo("false");
        });
}

Pattern: Stream-Based Property Creation

@Test
void testFromStream() {
    List<String> configKeys = List.of("key1", "key2", "key3");

    TestPropertyValues props = TestPropertyValues.of(
        configKeys.stream()
            .map(key -> key + "=value-" + key)
    );

    new ApplicationContextRunner()
        .run(context -> {
            props.applyTo(context);

            assertThat(context.getEnvironment().getProperty("key1"))
                .isEqualTo("value-key1");
            assertThat(context.getEnvironment().getProperty("key3"))
                .isEqualTo("value-key3");
        });
}

Pattern: Stream with Custom Mapper (since 2.4.0)

// Define a configuration object
record ServerConfig(String host, int port, String protocol) {}

@Test
void testStreamWithMapper() {
    List<ServerConfig> servers = List.of(
        new ServerConfig("localhost", 8080, "http"),
        new ServerConfig("api.example.com", 443, "https")
    );

    // Map objects to properties using custom mapper
    TestPropertyValues props = TestPropertyValues.of(
        servers.stream(),
        server -> TestPropertyValues.Pair.of(
            "server." + server.host() + ".port",
            String.valueOf(server.port())
        )
    );

    new ApplicationContextRunner()
        .run(context -> {
            props.applyTo(context);

            assertThat(context.getEnvironment().getProperty("server.localhost.port"))
                .isEqualTo("8080");
            assertThat(context.getEnvironment().getProperty("server.api.example.com.port"))
                .isEqualTo("443");
        });
}

@Test
void testStreamAndWithMapper() {
    // Build properties dynamically from different sources
    Map<String, String> baseConfig = Map.of("app.name", "TestApp");
    List<String> features = List.of("auth", "cache", "metrics");

    TestPropertyValues props = TestPropertyValues.of(baseConfig)
        .and(features.stream(), feature ->
            TestPropertyValues.Pair.of("feature." + feature + ".enabled", "true")
        );

    new ApplicationContextRunner()
        .run(context -> {
            props.applyTo(context);

            assertThat(context.getEnvironment().getProperty("app.name"))
                .isEqualTo("TestApp");
            assertThat(context.getEnvironment().getProperty("feature.auth.enabled"))
                .isEqualTo("true");
            assertThat(context.getEnvironment().getProperty("feature.cache.enabled"))
                .isEqualTo("true");
        });
}

Pattern: Combining with @TestPropertySource

@SpringBootTest
@TestPropertySource(properties = {
    "base.property=base-value"
})
class CombinedPropertiesTest {

    @Autowired
    private ConfigurableApplicationContext context;

    @Test
    void testCombinedProperties() {
        // Add test-specific properties
        TestPropertyValues
            .of("additional.property=additional-value")
            .applyTo(context);

        assertThat(context.getEnvironment().getProperty("base.property"))
            .isEqualTo("base-value");
        assertThat(context.getEnvironment().getProperty("additional.property"))
            .isEqualTo("additional-value");
    }
}

Pattern: Reusable Property Sets

class ReusablePropertySets {

    static final TestPropertyValues H2_DATABASE = TestPropertyValues.of(
        "spring.datasource.url=jdbc:h2:mem:testdb",
        "spring.datasource.driverClassName=org.h2.Driver",
        "spring.jpa.database-platform=org.hibernate.dialect.H2Dialect"
    );

    static final TestPropertyValues REDIS_CACHE = TestPropertyValues.of(
        "spring.cache.type=redis",
        "spring.redis.host=localhost",
        "spring.redis.port=6379"
    );

    static final TestPropertyValues DEBUG_LOGGING = TestPropertyValues.of(
        "logging.level.root=DEBUG",
        "logging.level.org.springframework=DEBUG"
    );
}

@Test
void testWithReusableProperties() {
    new ApplicationContextRunner()
        .run(context -> {
            ReusablePropertySets.H2_DATABASE.applyTo(context);
            ReusablePropertySets.DEBUG_LOGGING.applyTo(context);

            assertThat(context.getEnvironment().getProperty("spring.datasource.url"))
                .contains("h2:mem");
            assertThat(context.getEnvironment().getProperty("logging.level.root"))
                .isEqualTo("DEBUG");
        });
}

Pattern: Testing with Special Characters

@Test
void testSpecialCharacters() {
    TestPropertyValues props = TestPropertyValues.of(
        "app.message=Hello, World!",
        "app.regex=\\d{3}-\\d{4}",
        "app.json={\"key\":\"value\"}"
    );

    new ApplicationContextRunner()
        .run(context -> {
            props.applyTo(context);

            assertThat(context.getEnvironment().getProperty("app.message"))
                .contains("Hello");
            assertThat(context.getEnvironment().getProperty("app.regex"))
                .contains("\\d{3}");
        });
}

ApplicationContextTestUtils

Full Package: org.springframework.boot.test.util.ApplicationContextTestUtils

Utility for application context lifecycle management in tests.

public abstract class ApplicationContextTestUtils {

    /**
     * Closes this ApplicationContext and its parent hierarchy if any
     * Recursively closes parent contexts up the hierarchy
     * Safe to call with null context
     * @param context the context to close (can be null)
     * @since 1.4.0
     */
    public static void closeAll(@Nullable ApplicationContext context);
}

Pattern: Close Context Hierarchy

import org.springframework.boot.test.util.ApplicationContextTestUtils;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

@Test
void testCloseContextHierarchy() {
    // Create parent context
    ConfigurableApplicationContext parent =
        new AnnotationConfigApplicationContext(ParentConfig.class);

    // Create child context
    ConfigurableApplicationContext child =
        new AnnotationConfigApplicationContext();
    child.setParent(parent);
    child.register(ChildConfig.class);
    child.refresh();

    // Close entire hierarchy (child and parent)
    ApplicationContextTestUtils.closeAll(child);

    assertThat(child.isActive()).isFalse();
    assertThat(parent.isActive()).isFalse();
}

Troubleshooting

Issue: Properties not applied

  • Cause: Applied after context refresh
  • Solution: Apply before context.refresh() or use context runner

Issue: Property format error

  • Cause: Wrong separator (: instead of =)
  • Solution: Use "key=value" format

See Also

  • Integration Testing - properties attribute
  • Context Runners - withPropertyValues()