Spring Framework
Unit Testing
Java
Software Development
Programming

Populating Spring @Value during Unit Test

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

Introduction

@Value fields are populated by the Spring container, not by plain Java object construction. That means the right testing strategy depends on what kind of test you are writing: a true unit test that instantiates the class directly, or a Spring-powered test that starts enough of the container to resolve properties.

In a Plain Unit Test, Spring Does Nothing

If you write:

java
MyService service = new MyService();

then Spring is not involved, so fields annotated with @Value stay unset. That is expected. A plain unit test only sees normal Java behavior.

This is one reason constructor injection is often cleaner than field injection. You can still use @Value in the Spring-managed constructor, but the unit test can pass the value directly:

java
1import org.springframework.beans.factory.annotation.Value;
2import org.springframework.stereotype.Component;
3
4@Component
5public class ApiClient {
6    private final String baseUrl;
7
8    public ApiClient(@Value("${client.base-url}") String baseUrl) {
9        this.baseUrl = baseUrl;
10    }
11
12    public String getBaseUrl() {
13        return baseUrl;
14    }
15}

Plain unit test:

java
1import static org.junit.jupiter.api.Assertions.assertEquals;
2import org.junit.jupiter.api.Test;
3
4class ApiClientTest {
5    @Test
6    void constructorCanBeTestedWithoutSpring() {
7        ApiClient client = new ApiClient("http://localhost:9000");
8        assertEquals("http://localhost:9000", client.getBaseUrl());
9    }
10}

That is the fastest and simplest option when all you need is the configured value.

Use Spring Test Support When You Want Property Resolution

If the test is meant to verify Spring wiring itself, load a Spring context and provide the property through test configuration.

Example with @SpringBootTest:

java
1import static org.junit.jupiter.api.Assertions.assertEquals;
2import org.junit.jupiter.api.Test;
3import org.springframework.beans.factory.annotation.Autowired;
4import org.springframework.boot.test.context.SpringBootTest;
5
6@SpringBootTest(properties = "client.base-url=http://test-host")
7class ApiClientSpringTest {
8
9    @Autowired
10    private ApiClient client;
11
12    @Test
13    void valueIsInjectedFromTestProperties() {
14        assertEquals("http://test-host", client.getBaseUrl());
15    }
16}

This approach proves that Spring can resolve the placeholder and build the bean the way the application would.

You can also use @TestPropertySource when that style fits the project better:

java
1@SpringBootTest
2@TestPropertySource(properties = "client.base-url=http://test-host")
3class ApiClientSpringTest {
4}

Reflection Works, but It Is a Compromise

If a class still uses field injection and you want a very lightweight test, ReflectionTestUtils can set the field manually:

java
1import static org.junit.jupiter.api.Assertions.assertEquals;
2import org.junit.jupiter.api.Test;
3import org.springframework.test.util.ReflectionTestUtils;
4
5class LegacyService {
6    private String mode;
7
8    String getMode() {
9        return mode;
10    }
11}
12
13class LegacyServiceTest {
14    @Test
15    void canSetFieldDirectly() {
16        LegacyService service = new LegacyService();
17        ReflectionTestUtils.setField(service, "mode", "test");
18        assertEquals("test", service.getMode());
19    }
20}

This is useful for legacy code, but it is usually a sign that constructor injection would make the class easier to test.

Choose the Smallest Test That Proves the Right Thing

Ask what the test is actually trying to prove:

  • If you want to test business logic, pass the value directly and avoid starting Spring.
  • If you want to test configuration and bean wiring, start Spring and provide test properties.
  • If you are stuck with legacy field injection, reflection can bridge the gap.

Using a full application context for every class-level unit test slows the suite down and blurs the difference between unit tests and integration tests.

Common Pitfalls

The biggest mistake is expecting @Value to work in a plain unit test without Spring. It will not, because the container never ran.

Another common issue is loading the full Spring context just to test a small piece of logic that could have been exercised with direct constructor arguments.

Field injection is also a common source of testing friction. It hides dependencies and makes simple tests harder than they need to be.

Finally, property names in tests must match the placeholder exactly. If the bean expects client.base-url, setting client.url will not populate the field you care about.

Summary

  • '@Value is resolved by Spring, not by plain object construction.'
  • For true unit tests, constructor injection usually makes the class easiest to test.
  • Use @SpringBootTest or @TestPropertySource when you want Spring to resolve test properties.
  • 'ReflectionTestUtils can help with legacy field injection, but it is not the cleanest long-term design.'
  • Pick the lightest test setup that proves the behavior you actually care about.

Related reading
Course
Intermediate
27 lessons
14 hours
OOD Fundamentals

Master object-oriented design from first principles, SOLID, design patterns, and classic interview problems with hands-on coding.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

All Rights Reserved.