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:
@Spy Foo spyOnFoo = new Foo("argument");
passes a constant, literal value to the constructor of Foo, whereas
@Mock QuestionInfoRepository repository;
@Spy QuestionInfoService questionInfoService = new QuestionInfoService(repository);
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 repository
was null at initialization time, the QuestionInfoService
instance was already created with a null
repository and then assigned to the field. Later assignments to repository
have zero effect on the existing QuestionInfoService instance.
The solution? Manually instruct Mockito to create the spy in your setup method:
@Mock QuestionInfoRepository repository;
QuestionInfoService questionInfoService;
@Before
void setup() {
// here, `repository` has already been assigned a value by Mockito via the annotation:
questionInfoService = spy(new QuestionInfoService(repository));
}
A similar problem (albeit without @Spy
annotation), its cause and the solution is outlined in Corollary: Object life cycles and magic framework annotations.
References: