'Mockito cannot create Spy of @Autowired Spring-Data Repository
I am trying to overlay my whole test environment with Mockito.spy functionality so whenever I want i can stub a method but all other calls go to default functionality. This worked very well with the Service layer but I have problems with the Repository layer.
My setup is as follows:
Mockito - 2.15.0 Spring - 5.0.8 SpringBoot - 2.0.4
Repository:
public interface ARepository extends CrudRepository<ADBO, Long> {}
Service:
@Service
public class AService {
@Autowired
ARepository aRepository;
public ADBO getById(long id) {
return aRepository.findById(id).orElse(null);
}
public Iterable<ADBO> getAll() {
return aRepository.findAll();
}
}
The configuration for the spy:
@Profile("enableSpy")
@Configuration
public class SpyConfig {
@Bean
@Primary
public ARepository aRepository() {
return Mockito.spy(ARepository.class);
}
}
And my test class:
@ActiveProfiles("enableSpy")
@RunWith(SpringRunner.class)
@SpringBootTest
public class AServiceTest {
@Autowired
AService aService;
@Autowired
ARepository aRepository;
@Test
public void test() {
ADBO foo = new ADBO();
foo.setTestValue("bar");
aRepository.save(foo);
doReturn(Optional.of(new ADBO())).when(aRepository).findById(1L);
System.out.println("result (1):" + aService.getById(1));
System.out.println("result all:" + aService.getAll());
}
}
Now there are three possible outcomes to this test:
- aRepository is neither a mock nor a spy:
org.mockito.exceptions.misusing.NotAMockException: Argument passed to when() is not a mock! Example of corr... aRepository is a mock but not a spy (this is the result I get):
result (1):ADBO(id=null, testValue=null) result all:[]aRepository is a spy (this is what I want):
result (1):ADBO(id=null, testValue=null) result all:[ADBO(id=1, testValue=bar)]
I attribute this behavior to the fact that the spring instantiation of the repository is more complex in the background and the repository is not correctly instantiated when calling Mockito.spy(ARepository.class).
I have also tried autowireing the proper instance into the Configuration and calling Mockito.spy() with the @Autowired object.
This results in:
Cannot mock/spy class com.sun.proxy.$Proxy75
Mockito cannot mock/spy because :
- final class
According to my research Mockito can mock and spy final classes since v2.0.0.
Calling Mockito.mockingDetails(aRepository).isSpy() returns true which leads me to think the object in the background was not correctly instantiated.
Finally my question:
How do I get a spy instance of a Spring-Data Repository in my UnitTest with @Autowired?
Solution 1:[1]
You should use @SpyBean from spring-boot-test.
This used to be broken (per Spring Boot issue #7033) but it's now been fixed and supported since Spring Boot 2.5.3.
If you still getting this error and can't update your Spring Boot version, you might want to try the work-around proposed by @Mateusz Zaj?c in his answer to this question.
Solution 2:[2]
@SpyBean now works with spring data repositories since spring boot version 2.5.3
If you can't upgrade you can work around this by instantiating your spy as such in the test method body:
var yourSpy = Mockito.mock(FooRepository.class, AdditionalAnswers.delegatesTo(real))
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | M. Justin |
| Solution 2 | Mateusz Zaj?c |
