Servcice.java (class to test)
class Service {
@Autowired
private List<Metric> dependency1;
@Autowired
private Executor dependency2;
}
Metric.java : interface
interface Metric{
public void fetchMetric();
}
class Metric1 implements Metric{
public void fetchMetric() {}
}
class Metric2 implements Metric{
public void fetchMetric() {}
}
ServiceTest.java : (test class)
@ExtendWith(MockitoExtension.class)
class ServiceTest {
@Spy
private List<Metric> dependency1;
@Mock
private Executor dependency2;
@InjectMocks // class under test // this has above two as their dependencies.
Service service;
@Mock
private Metric1 metric1;
@Mock
private Metric2 metric2;
@BeforeEach
void setUp() {
// intializing the spy object list with mocks.
this.dependency1 = Arrays.asList(metric1,
metric2
);
}
@Test
void someTest() {
// here in debug mode I can see that `dependency1` as a **spy** and 'dependency1' present in the 'Service' are different, though they should be same.
}
}
Why @InjectMock
is not able to Inject a @Spy
List dependencies in the Service
class object? Am I missing something here.
dependency1
as a spy and dependency1
present as a part of the Service
are shown as two different objects on the de-bugger and making the test cases fail. I thought they should be the same.
Should we not initialise the Spies in
@BeforeEach
method ?
CodePudding user response:
The problem is you use @ExtendWith(MockitoExtension.class)
to tell mockito to instantiate your @Mocks @Spy and @InjectMocks. So after Mockito does this you change the field ServiceTest.dependency1 to be a list.
But since Mockito instantiated your CUT with the annotated dependencies, it is not updated when you change the ServiceTest.dependency1.
I would recommend to change Service to have a constructor so you can use the recommended constructor Injection. That way you can make your test like this:
@ExtendWith(MockitoExtension.class)
class ServiceTest {
@Mock
private Executor dependency2;
Service service;
@Mock
private Metric1 metric1;
@Mock
private Metric2 metric2;
@BeforeEach
void setUp() {
// intializing the spy object list with mocks.
service = new Service(Arrays.asList(metric1,metric2), dependency2);
}
}
CodePudding user response:
One way to handle it:
Remove @Spy and use @Mock on dependency1.
And you will need to handle the List w.r.t. the testcase like:
Mockito.doReturn(metric1).when(dependency1).get(ArgumentMatchers.anyString());