What is the difference between @SpyBean
from org.springframework.boot.test.mock.mockito.SpyBean
and @Spy
from org.mockito.Spy
?
Using @SpyBean
instead of @Spy
makes my tests fail.
@Spy
doc says:
A field annotated with @Spy can be initialized explicitly at declaration point. Alternatively, if you don't provide the instance Mockito will try to find zero argument constructor (even private) and create an instance for you.
@SpyBean
doc says:
Annotation that can be used to apply Mockito spies to a Spring ApplicationContext.
All beans in the context of the same type will be wrapped with the spy. If no existing bean is defined a new one will be added.
So the main difference is @SpyBean
is a Spring Boot specific annotation but @Spy
is part of Mockito itself. @SpyBean
and @Spy
basically do the same, but @SpyBean
can resolve the Spring specific dependencies, e.g. @Autowired
, @Spy
can only create object with empty constructor.
Something I noticed during testing is that SpyBean can result in certain values being carried over between tests whereas Spy always starts from a clean slate. In my case I had @SpyBean set on a class that initially had autowired components. But after I had refactored the class to remove them I did not change the test classes and I had an unexpected failure.
Class1 {
boolean boo;
myMethodA() {
if (something) {
boo=true;
}
}
myMethodB() {
if (boo) {
doThis();
} else {
doThat();
}
}
}
Both Test1 and Test2 used the @SpyBean Class1. Both Test1 and Test2 ran successfully in isolation but when run as a suite Test2 would fail if it ran after Test1.
Test1 set a boolean value in Class1 to true, which was still true when Test2 ran. This caused Test2 to fail as it was expecting the boolean to be false. Changing to @Spy Class1 resulted in the boolean being reset to false and both tests passing.