This is an issue when a test class manually instantiates the object under test in a setup method by passing mock-annotated fields as constructor arguments, instead of using the test framework’s automatic dependency injection annotation.
In Java with Mockito, this specifically refers to instantiating the object under test manually instead of using the @InjectMocks
annotation, in a class annotated with @ExtendWith(MockitoExtension.class) (JUnit 5) or @RunWith(MockitoJUnitRunner.class) (JUnit 4).
When writing unit tests with mocking frameworks, developers often need to inject mock dependencies into the object being tested. While it’s possible to manually construct this object by passing mock-annotated fields as constructor arguments in a test setup method, this approach creates unnecessary boilerplate code.
Mocking frameworks provide dependency injection annotations specifically for this purpose. These annotations automatically inject all mock and spy annotated fields into the object under test, eliminating the need for manual construction.
The manual approach has several drawbacks:
By contrast, automatic dependency injection annotations automatically adapt to constructor changes, keep test code concise, and clearly signal which object is under test.
In Mockito specifically, use the @InjectMocks annotation to mark the object under test, and @Mock or @Spy
annotations for dependencies.
This issue impacts code maintainability. Tests with manual object construction require more effort to write and update, especially during refactoring. While the tests still function correctly, the extra boilerplate increases the cognitive load on developers and makes the codebase harder to maintain over time.
For JUnit 4 tests using @RunWith(MockitoJUnitRunner.class), replace manual construction in a setup method with the
@InjectMocks annotation.
@RunWith(MockitoJUnitRunner.class)
public class OrderProcessorTest {
@Mock
private PaymentGateway paymentGateway;
@Mock
private InventoryService inventoryService;
private OrderProcessor orderProcessor;
@Before
public void setUp() {
orderProcessor = new OrderProcessor(paymentGateway, inventoryService); // Noncompliant
}
@Test
public void testProcessOrder() {
// test implementation
}
}
@RunWith(MockitoJUnitRunner.class)
public class OrderProcessorTest {
@Mock
private PaymentGateway paymentGateway;
@Mock
private InventoryService inventoryService;
@InjectMocks
private OrderProcessor orderProcessor;
@Test
public void testProcessOrder() {
// test implementation
}
}
For JUnit 5 tests using MockitoExtension, replace manual construction with @InjectMocks.
@ExtendWith(MockitoExtension.class)
public class OrderProcessorTest {
@Mock
private PaymentGateway paymentGateway;
@Mock
private InventoryService inventoryService;
private OrderProcessor orderProcessor;
@BeforeEach
public void setUp() {
orderProcessor = new OrderProcessor(paymentGateway, inventoryService); // Noncompliant
}
@Test
public void testProcessOrder() {
// test implementation
}
}
@ExtendWith(MockitoExtension.class)
public class OrderProcessorTest {
@Mock
private PaymentGateway paymentGateway;
@Mock
private InventoryService inventoryService;
@InjectMocks
private OrderProcessor orderProcessor;
@Test
public void testProcessOrder() {
// test implementation
}
}