I have a class:
@AllArgsConstructor
public class QuestionInfoService {
QuestionInfoRepository repository;
}
I would like to create a test where I create a spy for this class:
@Spy QuestionInfoService questionInfoService
At the same time I would like questionInfoService to be initialized with mocked QuestionInfoRepository :
@RunWith(MockitoJUnitRunner.class)
public class QuestionInfoServiceTest {
@Mock
QuestionInfoRepository repository;
@Spy
QuestionInfoService questionInfoService = new QuestionInfoService(repository);
@Test
public void shouldFetchQuestions() {
System.out.println(questionInfoService.getRepository());
}
}
According to documentation I should be able to do that:
@Spy Foo spyOnFoo = new Foo("argument");
https://javadoc.io/doc/org.mockito/mockito-core/2.8.47/org/mockito/Spy.html
Although when I run my tests the results are:
null
How can I initialize the @Spy with the field annotated with @Mock ?
There's a small but crucial difference between your test and the example from the Mockito docs:
passes a constant, literal value to the constructor of Foo, whereas
passes the value of an uninitialized field to the constructor of QuestionInfoService.
Mockito/JUnit need to create an instance of your test class (one instance per test method). The field initializers are executed at the time of initialization, and only after that can Mockito assign values to the annotated fields. Since
repositorywas null at initialization time, theQuestionInfoServiceinstance was already created with anullrepository and then assigned to the field. Later assignments torepositoryhave zero effect on the existing QuestionInfoService instance.The solution? Manually instruct Mockito to create the spy in your setup method:
A similar problem (albeit without
@Spyannotation), its cause and the solution is outlined in Corollary: Object life cycles and magic framework annotations.References: