@SpyBean vs @MockBean in Spring Boot
Fast recall
Quick revision
Core answer
@MockBean replaces a bean in the test ApplicationContext with a Mockito mock; @SpyBean wraps an existing bean with a spy so unstubbed calls use real behavior. Since Spring Boot 3.4 these annotations are deprecated for removal in 4.0 in favor of Spring Framework's @MockitoBean and @MockitoSpyBean. A bean override changes the context cache key, but it does not automatically mark every context dirty or force a reload for every test class; classes with the same configuration can reuse a cached context.Interview-ready guide
Interview answer
A Spring context mock replaces a bean with a Mockito mock, so unstubbed methods return Mockito defaults. A context spy wraps the existing bean, so unstubbed methods run the real implementation and only selected behavior is overridden or verified.
The older Spring Boot annotations are @MockBean and @SpyBean. They were deprecated in Spring Boot 3.4 for removal in 4.0 in favor of Spring Framework's @MockitoBean and @MockitoSpyBean. The choice still depends on whether the test wants a complete replacement or mostly real behavior.
Bean overrides form part of the test context configuration. Different override combinations can produce different cache keys and slow a suite by creating more contexts, but they do not automatically dirty every context. Equivalent test configurations can reuse a cached context. With a spy, I use the doReturn or doThrow style when normal when would execute the real bean during setup.
@SpyBean vs @MockBean in Spring Boot
Direct Answer
The older Spring Boot @MockBean replaces a bean in the test ApplicationContext with a Mockito mock; @SpyBean wraps an existing bean with a spy so unstubbed calls use real behavior. Since Spring Boot 3.4 these annotations are deprecated for removal in 4.0 in favor of Spring Framework's @MockitoBean and @MockitoSpyBean. A bean override changes the context cache key, but it does not automatically mark every context dirty or force a reload for every test class; classes with the same configuration can reuse a cached context.
Code Example
@WebMvcTest(OrderController.class)
class OrderControllerTest {
@MockitoBean OrderRepository repository; // mock bean in the context
@MockitoSpyBean AuditService audit; // wraps the real bean
@Test
void recordsTheRead() {
when(repository.findById(1L)).thenReturn(Optional.of(new Order(1L)));
// Exercise the controller before verification.
verify(audit).record(any());
}
}
Common Mistakes & Pitfalls
- Using context overrides for plain unit tests: direct constructor wiring is enough when Spring behavior is irrelevant.
- Spying an unsafe bean: unstubbed methods can perform real external work.
- Using normal
whenon a risky spy call: it executes the real method during setup; use ado...whenform when needed. - Calling every cache difference invalidation: override metadata can create a different cache key, but does not automatically mark a context dirty.
- Using legacy annotations in new code: current Spring uses
@MockitoBeanand@MockitoSpyBean.
Check your understanding
Answer these without looking back at the article.
- How does Spring's context caching interact with @MockBean?
- Can you use @SpyBean on a Spring Data repository?
Looking for a structured roadmap?
Every module on this page is also part of the full Java backend interview track.