|
This version is still in development and is not considered stable yet. For the latest stable version, please use Spring Framework 7.0.9! |
@MockitoBean and @MockitoSpyBean
@MockitoBean and
@MockitoSpyBean
can be used in test classes to override a bean in the test’s ApplicationContext with a
Mockito mock or spy, respectively. In the latter case, an early instance of the
original bean is captured and wrapped by the spy.
The annotations can be applied in the following ways.
-
On a non-static field in a test class or any of its superclasses.
-
On a non-static field in an enclosing class for a
@Nestedtest class or in any class in the type hierarchy or enclosing class hierarchy above the@Nestedtest class. -
On a parameter in the constructor for a test class.
-
At the type level on a test class or any superclass or implemented interface in the type hierarchy above the test class.
-
At the type level on an enclosing class for a
@Nestedtest class or on any class or interface in the type hierarchy or enclosing class hierarchy above the@Nestedtest class.
When @MockitoBean or @MockitoSpyBean is declared on a field or constructor parameter,
the bean to mock or spy is inferred from the type of the annotated field or parameter. If
multiple candidates exist in the ApplicationContext, a @Qualifier annotation can be
declared on the field or parameter to help disambiguate. In the absence of a @Qualifier
annotation, the name of the annotated field or parameter will be used as a fallback
qualifier. Alternatively, you can explicitly specify a bean name to mock or spy by
setting the value or name attribute in the annotation.
When @MockitoBean or @MockitoSpyBean is declared at the type level, the type of bean
(or beans) to mock or spy must be supplied via the types attribute in the annotation –
for example, @MockitoBean(types = {OrderService.class, UserService.class}). If multiple
candidates exist in the ApplicationContext, you can explicitly specify a bean name to
mock or spy by setting the name attribute. Note, however, that the types attribute
must contain a single type if an explicit bean name is configured – for example,
@MockitoBean(name = "ps1", types = PrintingService.class).
To support reuse of mock configuration, @MockitoBean and @MockitoSpyBean may be used
as meta-annotations to create custom composed annotations – for example, to define
common mock or spy configuration in a single annotation that can be reused across a test
suite. @MockitoBean and @MockitoSpyBean can also be used as repeatable annotations at
the type level — for example, to mock or spy several beans by name.
|
Qualifiers, including the name of a field, are used to determine if a separate
|
|
Using See context hierarchies with bean overrides for further details and examples. |
Each annotation also defines Mockito-specific attributes to fine-tune the mocking behavior.
The @MockitoBean annotation uses the REPLACE_OR_CREATE
strategy for bean overrides.
If a corresponding bean does not exist, a new bean will be created. However, you can
switch to the REPLACE strategy by setting the enforceOverride attribute to true –
for example, @MockitoBean(enforceOverride = true). Because this strategy replaces the
bean directly, bypassing the container’s normal bean post-processing, the resulting mock
is a bare object: it is never wrapped in a Spring AOP proxy, even if the original bean
would have been — for example, due to @Transactional, @Cacheable, or @Retryable. See
Bean
Overrides and Spring AOP Proxies for details.
The @MockitoSpyBean annotation uses the WRAP
strategy:
an early instance of the original bean is captured and used to create a Mockito spy.
This strategy requires that exactly one candidate bean exists. In contrast to
@MockitoBean, if the original bean would have been wrapped in a Spring AOP proxy, that
proxy is still created — but it now wraps the spy instead of the original bean. See
@MockitoSpyBean and Spring AOP Proxies
for a diagram and further details on the consequences this has for stubbing and
verification.
|
As stated in the documentation for Mockito, there are times when using To avoid such undesired side effects, consider using
|
|
When using Similarly, when using When using Similarly, when using Furthermore, |
|
There are no restrictions on the visibility of Such fields can therefore be |
@MockitoBean Examples
The following example shows how to use the default behavior of the @MockitoBean
annotation.
-
Java
-
Kotlin
@SpringJUnitConfig(TestConfig.class)
class BeanOverrideTests {
@MockitoBean (1)
CustomService customService;
// tests...
}
| 1 | Replace the bean with type CustomService with a Mockito mock. |
@SpringJUnitConfig(TestConfig::class)
class BeanOverrideTests {
@MockitoBean (1)
lateinit var customService: CustomService
// tests...
}
| 1 | Replace the bean with type CustomService with a Mockito mock. |
In the example above, we are creating a mock for CustomService. If more than one bean
of that type exists, the bean named customService is considered. Otherwise, the test
will fail, and you will need to provide a qualifier of some sort to identify which of the
CustomService beans you want to override. If no such bean exists, a bean will be
created with an auto-generated bean name.
The following example uses a by-name lookup, rather than a by-type lookup. If no bean
named service exists, one is created.
-
Java
-
Kotlin
@SpringJUnitConfig(TestConfig.class)
class BeanOverrideTests {
@MockitoBean("service") (1)
CustomService customService;
// tests...
}
| 1 | Replace the bean named service with a Mockito mock. |
@SpringJUnitConfig(TestConfig::class)
class BeanOverrideTests {
@MockitoBean("service") (1)
lateinit var customService: CustomService
// tests...
}
| 1 | Replace the bean named service with a Mockito mock. |
The following example shows how to use @MockitoBean on a constructor parameter for a
by-type lookup.
-
Java
-
Kotlin
@SpringJUnitConfig(TestConfig.class)
class BeanOverrideTests {
private final CustomService customService;
BeanOverrideTests(@MockitoBean CustomService customService) { (1)
this.customService = customService;
}
// tests...
}
| 1 | Replace the bean with type CustomService with a Mockito mock and inject it into
the constructor. |
@SpringJUnitConfig(TestConfig::class)
class BeanOverrideTests(@MockitoBean val customService: CustomService) { (1)
// tests...
}
| 1 | Replace the bean with type CustomService with a Mockito mock and inject it into
the constructor. |
The following example shows how to use @MockitoBean on a constructor parameter for a
by-name lookup.
-
Java
-
Kotlin
@SpringJUnitConfig(TestConfig.class)
class BeanOverrideTests {
private final CustomService customService;
BeanOverrideTests(@MockitoBean("service") CustomService customService) { (1)
this.customService = customService;
}
// tests...
}
| 1 | Replace the bean named service with a Mockito mock and inject it into the
constructor. |
@SpringJUnitConfig(TestConfig::class)
class BeanOverrideTests(@MockitoBean("service") val customService: CustomService) { (1)
// tests...
}
| 1 | Replace the bean named service with a Mockito mock and inject it into the
constructor. |
The following @SharedMocks annotation registers two mocks by-type and one mock by-name.
-
Java
-
Kotlin
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@MockitoBean(types = {OrderService.class, UserService.class}) (1)
@MockitoBean(name = "ps1", types = PrintingService.class) (2)
public @interface SharedMocks {
}
| 1 | Register OrderService and UserService mocks by-type. |
| 2 | Register PrintingService mock by-name. |
@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.RUNTIME)
@MockitoBean(types = [OrderService::class, UserService::class]) (1)
@MockitoBean(name = "ps1", types = [PrintingService::class]) (2)
annotation class SharedMocks
| 1 | Register OrderService and UserService mocks by-type. |
| 2 | Register PrintingService mock by-name. |
The following demonstrates how @SharedMocks can be used on a test class.
-
Java
-
Kotlin
@SpringJUnitConfig(TestConfig.class)
@SharedMocks (1)
class BeanOverrideTests {
@Autowired OrderService orderService; (2)
@Autowired UserService userService; (2)
@Autowired PrintingService ps1; (2)
// Inject other components that rely on the mocks.
@Test
void testThatDependsOnMocks() {
// ...
}
}
| 1 | Register common mocks via the custom @SharedMocks annotation. |
| 2 | Optionally inject mocks to stub or verify them. |
@SpringJUnitConfig(TestConfig::class)
@SharedMocks (1)
class BeanOverrideTests {
@Autowired
lateinit var orderService: OrderService (2)
@Autowired
lateinit var userService: UserService (2)
@Autowired
lateinit var ps1: PrintingService (2)
// Inject other components that rely on the mocks.
@Test
fun testThatDependsOnMocks() {
// ...
}
}
| 1 | Register common mocks via the custom @SharedMocks annotation. |
| 2 | Optionally inject mocks to stub or verify them. |
The mocks can also be injected into @Configuration classes or other test-related
components in the ApplicationContext in order to configure them with Mockito’s stubbing
APIs.
|
@MockitoSpyBean Examples
The following example shows how to use the default behavior of the @MockitoSpyBean
annotation.
-
Java
-
Kotlin
@SpringJUnitConfig(TestConfig.class)
class BeanOverrideTests {
@MockitoSpyBean (1)
CustomService customService;
// tests...
}
| 1 | Wrap the bean with type CustomService with a Mockito spy. |
@SpringJUnitConfig(TestConfig::class)
class BeanOverrideTests {
@MockitoSpyBean (1)
lateinit var customService: CustomService
// tests...
}
| 1 | Wrap the bean with type CustomService with a Mockito spy. |
In the example above, we are wrapping the bean with type CustomService. If more than
one bean of that type exists, the bean named customService is considered. Otherwise,
the test will fail, and you will need to provide a qualifier of some sort to identify
which of the CustomService beans you want to spy.
The following example uses a by-name lookup, rather than a by-type lookup.
-
Java
-
Kotlin
@SpringJUnitConfig(TestConfig.class)
class BeanOverrideTests {
@MockitoSpyBean("service") (1)
CustomService customService;
// tests...
}
| 1 | Wrap the bean named service with a Mockito spy. |
@SpringJUnitConfig(TestConfig::class)
class BeanOverrideTests {
@MockitoSpyBean("service") (1)
lateinit var customService: CustomService
// tests...
}
| 1 | Wrap the bean named service with a Mockito spy. |
The following example shows how to use @MockitoSpyBean on a constructor parameter for
a by-type lookup.
-
Java
-
Kotlin
@SpringJUnitConfig(TestConfig.class)
class BeanOverrideTests {
private final CustomService customService;
BeanOverrideTests(@MockitoSpyBean CustomService customService) { (1)
this.customService = customService;
}
// tests...
}
| 1 | Wrap the bean with type CustomService with a Mockito spy and inject it into the
constructor. |
@SpringJUnitConfig(TestConfig::class)
class BeanOverrideTests(@MockitoSpyBean val customService: CustomService) { (1)
// tests...
}
| 1 | Wrap the bean with type CustomService with a Mockito spy and inject it into the
constructor. |
The following example shows how to use @MockitoSpyBean on a constructor parameter for
a by-name lookup.
-
Java
-
Kotlin
@SpringJUnitConfig(TestConfig.class)
class BeanOverrideTests {
private final CustomService customService;
BeanOverrideTests(@MockitoSpyBean("service") CustomService customService) { (1)
this.customService = customService;
}
// tests...
}
| 1 | Wrap the bean named service with a Mockito spy and inject it into the constructor. |
@SpringJUnitConfig(TestConfig::class)
class BeanOverrideTests(@MockitoSpyBean("service") val customService: CustomService) { (1)
// tests...
}
| 1 | Wrap the bean named service with a Mockito spy and inject it into the constructor. |
The following @SharedSpies annotation registers two spies by-type and one spy by-name.
-
Java
-
Kotlin
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@MockitoSpyBean(types = {OrderService.class, UserService.class}) (1)
@MockitoSpyBean(name = "ps1", types = PrintingService.class) (2)
public @interface SharedSpies {
}
| 1 | Register OrderService and UserService spies by-type. |
| 2 | Register PrintingService spy by-name. |
@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.RUNTIME)
@MockitoSpyBean(types = [OrderService::class, UserService::class]) (1)
@MockitoSpyBean(name = "ps1", types = [PrintingService::class]) (2)
annotation class SharedSpies
| 1 | Register OrderService and UserService spies by-type. |
| 2 | Register PrintingService spy by-name. |
The following demonstrates how @SharedSpies can be used on a test class.
-
Java
-
Kotlin
@SpringJUnitConfig(TestConfig.class)
@SharedSpies (1)
class BeanOverrideTests {
@Autowired OrderService orderService; (2)
@Autowired UserService userService; (2)
@Autowired PrintingService ps1; (2)
// Inject other components that rely on the spies.
@Test
void testThatDependsOnMocks() {
// ...
}
}
| 1 | Register common spies via the custom @SharedSpies annotation. |
| 2 | Optionally inject spies to stub or verify them. |
@SpringJUnitConfig(TestConfig::class)
@SharedSpies (1)
class BeanOverrideTests {
@Autowired
lateinit var orderService: OrderService (2)
@Autowired
lateinit var userService: UserService (2)
@Autowired
lateinit var ps1: PrintingService (2)
// Inject other components that rely on the spies.
@Test
fun testThatDependsOnMocks() {
// ...
}
}
| 1 | Register common spies via the custom @SharedSpies annotation. |
| 2 | Optionally inject spies to stub or verify them. |
The spies can also be injected into @Configuration classes or other test-related
components in the ApplicationContext in order to configure them with Mockito’s stubbing
APIs.
|
@MockitoSpyBean and Spring AOP Proxies
As explained in
Bean
Overrides and Spring AOP Proxies, if the bean being spied on would normally be wrapped in
a Spring AOP proxy — for example, due to @Transactional, @Cacheable, or @Retryable
— that proxy is still created, with the spy as its target. The bean injected into the
test class and into other beans in the ApplicationContext is therefore the proxy, not
the spy itself.
Verification via Mockito’s verify() API is unaffected by this and works transparently,
regardless of whether it is invoked on the proxy or on the underlying spy.
Stubbing Through the Proxy
Stubbing requires more care than verification, since Mockito.doReturn(…).when(…),
Mockito.doThrow(…).when(…), and similar methods behave differently depending on the
nature of the AOP advice involved when invoked on the proxy.
Since when is a reserved keyword in Kotlin, the Kotlin examples below use the
given(…), willReturn(…), and willThrow(…) methods from BDDMockito instead
of Mockito.doReturn(…).when(…) and Mockito.doThrow(…).when(…).
|
Advice that does not retain state between invocations — such as
@Retryable — has no adverse
effect on stubbing. The following stubbing sequence, invoked on the proxy, behaves exactly
as it would on the underlying spy directly, including triggering a retry when the thrown
exception is encountered.
-
Java
-
Kotlin
doReturn("ok")
.doThrow(new RuntimeException("Message delivery failed"))
.doReturn("ok again")
.when(clientService).sendMessage(any()); (1)
| 1 | clientService is the injected proxy. Since @Retryable advice is a stateless
pass-through, each call — including the one that throws — reaches the spy directly. |
willReturn("ok")
.willThrow(RuntimeException("Message delivery failed"))
.willReturn("ok again")
.given(clientService).sendMessage(any()) (1)
| 1 | clientService is the injected proxy. Since @Retryable advice is a stateless
pass-through, each call — including the one that throws — reaches the spy directly. |
Advice that caches or otherwise memoizes the outcome of an invocation — such as
@Cacheable — does not behave the same way. While a doReturn(…), doThrow(…), or
similar declaration is being recorded, Mockito does not invoke the spy’s real or
previously stubbed behavior; instead, the invocation used to declare the stubbing returns
an empty value (for example, null). If that invocation is made on the proxy, the caching
advice caches this empty value, which then permanently shadows the spy for that
combination of arguments — including for the very invocation that was supposed to
configure the stubbing.
-
Java
-
Kotlin
doReturn(1L).when(dateService).getDate(false); (1)
dateService.getDate(false); (2)
| 1 | dateService is the injected proxy. This invocation is intercepted by Mockito’s
stubbing infrastructure before it reaches the spy, so the caching advice ends up
caching an empty value for argument false. |
| 2 | Returns the empty value cached by the previous invocation — not 1L — because the
cache was already populated. |
willReturn(1L).given(dateService).getDate(false) (1)
dateService.getDate(false) (2)
| 1 | dateService is the injected proxy. This invocation is intercepted by Mockito’s
stubbing infrastructure before it reaches the spy, so the caching advice ends up
caching an empty value for argument false. |
| 2 | Returns the empty value cached by the previous invocation — not 1L — because the
cache was already populated. |
To avoid this, stub directly on the spy instead of on the proxy, by unwrapping the proxy
with
AopTestUtils.getUltimateTargetObject(…).
-
Java
-
Kotlin
DateService spy = AopTestUtils.getUltimateTargetObject(dateService);
doReturn(1L).when(spy).getDate(false);
val spy = AopTestUtils.getUltimateTargetObject<DateService>(dateService)
willReturn(1L).given(spy).getDate(false)
Disabling AOP Advice for Tests
Rather than working around the proxy as shown above, you may instead prefer to disable
the underlying AOP advice for the duration of the test, while keeping @Retryable,
@Cacheable, or similar annotations in place in production code. Common reasons include
avoiding retry delays that slow down the test suite, or avoiding caching altogether so
that every invocation reaches the spy directly — which also sidesteps the stubbing
pitfall described above, without having to unwrap the proxy at all.
The general technique is to externalize whatever controls the advice’s effective behavior
— for example, the number of retry attempts or the CacheManager backing @Cacheable
— and override that configuration for tests only, typically by using a bean override or a
test-specific property. The proxy and its advice are still created, but their behavior is
simply made a no-op or pure pass-through for the test.
For @Retryable, bind the maxRetriesString attribute to a property placeholder with a
sensible default (so that production configuration is unaffected if the property is not
set), and override that property in the test with
@TestPropertySource
so that no retries are attempted.
-
Java
-
Kotlin
@Retryable(maxRetriesString = "${sendMessage.maxRetries:3}", delay = 10)
public String sendMessage(String request) {
// ...
}
@Retryable(maxRetriesString = "\${sendMessage.maxRetries:3}", delay = 10)
fun sendMessage(request: String): String {
// ...
}
-
Java
-
Kotlin
@SpringJUnitConfig
@TestPropertySource(properties = "sendMessage.maxRetries = 0") (1)
class ClientServiceTests {
@MockitoSpyBean
ClientService clientService;
// test case body...
}
| 1 | With no retries permitted, the first (and only) attempt is made, and a thrown
exception propagates immediately, so the spy’s stubbing chain behaves exactly as
declared, including for doThrow(…) answers. |
@SpringJUnitConfig
@TestPropertySource(properties = ["sendMessage.maxRetries = 0"]) (1)
class ClientServiceTests {
@MockitoSpyBean
lateinit var clientService: ClientService
// test case body...
}
| 1 | With no retries permitted, the first (and only) attempt is made, and a thrown
exception propagates immediately, so the spy’s stubbing chain behaves exactly as
declared, including for doThrow(…) answers. |
For @Cacheable, Spring provides
NoOpCacheManager — a
CacheManager that accepts cache entries but never actually stores them, so every
invocation results in a cache miss and therefore an invocation of the target method.
Overriding the CacheManager bean with a NoOpCacheManager — for example, with
@TestBean —
effectively disables caching for the test without touching the @Cacheable annotation in
production code.
-
Java
-
Kotlin
@SpringJUnitConfig
class DateServiceTests {
@MockitoSpyBean
DateService dateService;
@TestBean (1)
CacheManager cacheManager;
static CacheManager cacheManager() { (2)
return new NoOpCacheManager();
}
@Test
void test() {
doReturn(1L).when(dateService).getDate(false);
assertThat(dateService.getDate(false)).isEqualTo(1L);
doReturn(2L).when(dateService).getDate(false);
assertThat(dateService.getDate(false)).isEqualTo(2L); (3)
}
}
| 1 | Override the CacheManager bean for this test. |
| 2 | Replace it with a NoOpCacheManager, so @Cacheable never actually caches anything. |
| 3 | No longer masked by a stale cache entry, since every call reaches the spy. |
@SpringJUnitConfig
class DateServiceTests {
@MockitoSpyBean
lateinit var dateService: DateService
@TestBean (1)
lateinit var cacheManager: CacheManager
companion object {
@JvmStatic
fun cacheManager(): CacheManager { (2)
return NoOpCacheManager()
}
}
@Test
fun test() {
willReturn(1L).given(dateService).getDate(false)
assertThat(dateService.getDate(false)).isEqualTo(1L)
willReturn(2L).given(dateService).getDate(false)
assertThat(dateService.getDate(false)).isEqualTo(2L) (3)
}
}
| 1 | Override the CacheManager bean for this test. |
| 2 | Replace it with a NoOpCacheManager, so @Cacheable never actually caches anything. |
| 3 | No longer masked by a stale cache entry, since every call reaches the spy. |