Field Injection and Unit Testing Problem
Fast recall
Quick revision
Core answer
Whether you understand the practical cost of field injection when writing fast, isolated unit tests.
Thinking field injection is just a style choice, not realizing it forces slow integration tests or reflection workarounds.
Contrast the two concretely: constructor injection is three lines of plain JUnit, field injection needs @SpringBootTest or ReflectionTestUtils.
Interview-ready guide
Interview answer
With @Autowired private OrderRepository repository, new OrderService() creates an object whose repository is null until Spring or reflection fills it. A test can use ReflectionTestUtils, but the constructor does not reveal that the object is incomplete. With OrderService(OrderRepository repository), the test passes a mock or fake directly and the class can reject null immediately. This improves design visibility; it is not a claim that every constructor-injected class is immutable.
Field Injection and Unit Testing Problem
Direct Answer
Field injection hides required dependencies from the constructor and prevents making those injected fields final. A plain test can still instantiate the class if it has a no-arg constructor, but it cannot supply private dependencies through the public API; it then needs reflection, package-visible mutation, or a Spring context. Constructor injection makes dependencies explicit and usually keeps the test simple.
Code Example
// Field injection - hard to test
@Service
class OrderService {
@Autowired
private OrderRepository repo; // private, can't set in a plain test
public Order createOrder(String item) {
return repo.save(new Order(item));
}
}
// Constructor injection - easy to test
@Service
class OrderServiceCtor {
private final OrderRepository repo;
public OrderServiceCtor(OrderRepository repo) { // explicit dependency
this.repo = repo;
}
public Order createOrder(String item) {
return repo.save(new Order(item));
}
}
// Unit test with constructor injection - no Spring
class OrderServiceTest {
@Test
void testCreateOrder() {
var mockRepo = Mockito.mock(OrderRepository.class);
var service = new OrderServiceCtor(mockRepo); // one line, no Spring
service.createOrder("Laptop");
verify(mockRepo).save(any());
}
}
Common Mistakes & Pitfalls
- Using @SpringBootTest for every service test: Freshers slap @SpringBootTest on each test class when using field injection, turning unit tests into slow integration tests that load the whole context to test one method.
- Thinking field injection is cleaner: A single @Autowired field looks shorter than a constructor, but it hides the dependencies and makes testing harder. The constructor shows what the class needs.
- Not knowing ReflectionTestUtils exists: Forced to test field-injected code, some give up on unit tests entirely instead of using ReflectionTestUtils.setField() — ugly, but an option when you inherit such code.
Check your understanding
Answer these without looking back at the article.
- How do you write a unit test for a class that uses field injection?
- What is @InjectMocks in Mockito?
- What is ReflectionTestUtils in Spring?
Looking for a structured roadmap?
Every module on this page is also part of the full Java backend interview track.