Isolate layers in Spring Boot tests using MockitoBean
MockitoBean
registers a Mockito mock in the Spring ApplicationContext
, replacing a real bean for the duration of a test. This enables isolation of layers (e.g., mocking a service when testing a controller) while still leveraging Spring’s test support.
@WebMvcTest
, @DataJpaTest
, and @SpringBootTest
MockitoBean
when you need Spring to autowire the mock into other beans under test.@Mock
for fast, framework-free unit tests of a class in isolation.@RestController
@RequestMapping("/api/books")
class BookController {
private final BookService bookService;
BookController(BookService bookService) {
this.bookService = bookService;
}
@GetMapping("/{id}")
public ResponseEntity<BookDto> get(@PathVariable Long id) {
return ResponseEntity.ok(bookService.getById(id));
}
}
interface BookService {
BookDto getById(Long id);
}
@WebMvcTest(BookController.class)
class BookControllerTest {
@Autowired
private MockMvc mockMvc;
@MockitoBean
private BookService bookService; // replaces real bean in the context
@Test
void should_return_book_by_id() throws Exception {
BookDto stub = new BookDto(1L, "DDD");
Mockito.when(bookService.getById(1L)).thenReturn(stub);
mockMvc.perform(MockMvcRequestBuilders.get("/api/books/1"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.id").value(1))
.andExpect(jsonPath("$.title").value("DDD"));
Mockito.verify(bookService).getById(1L);
}
}
In a full context test you can replace expensive/external dependencies with MockitoBean
while keeping the rest real.
@SpringBootTest
class BookServiceTest {
@Autowired
private BookServiceImpl bookService; // real service under test
@MockitoBean
private BookRepository bookRepository; // mock replaces real repository bean
@Test
void findById_returns_book() {
Book entity = new Book(1L, "Clean Code");
Mockito.when(bookRepository.findById(1L)).thenReturn(Optional.of(entity));
BookDto dto = bookService.getById(1L);
assertEquals(1L, dto.getId());
assertEquals("Clean Code", dto.getTitle());
Mockito.verify(bookRepository).findById(1L);
}
}
@Service
class CurrencyService {
private final RestTemplate restTemplate;
CurrencyService(RestTemplate restTemplate) {
this.restTemplate = restTemplate;
}
BigDecimal rate(String from, String to) {
ResponseEntity<BigDecimal> resp = restTemplate.getForEntity(
"/rates?from=" + from + "&to=" + to, BigDecimal.class);
return resp.getBody();
}
}
@SpringBootTest
class CurrencyServiceTest {
@Autowired
private CurrencyService currencyService;
@MockitoBean
private RestTemplate restTemplate;
@Test
void rate_returns_stubbed_value() {
Mockito.when(restTemplate.getForEntity(Mockito.anyString(), Mockito.eq(BigDecimal.class)))
.thenReturn(ResponseEntity.ok(new BigDecimal("1.10")));
assertEquals(new BigDecimal("1.10"), currencyService.rate("USD", "EUR"));
}
}
@SpyBean
wraps the actual Spring bean, invoking real methods unless stubbed. Useful for verifying interactions or overriding specific methods.
@SpringBootTest
class PricingServiceTest {
@SpyBean
private DiscountCalculator discountCalculator; // real bean, spied
@Autowired
private PricingService pricingService;
@Test
void calculates_total_with_spy_override() {
Mockito.doReturn(new BigDecimal("0.20"))
.when(discountCalculator).currentDiscount();
BigDecimal total = pricingService.totalFor(100);
Mockito.verify(discountCalculator).currentDiscount();
assertEquals(new BigDecimal("80.00"), total);
}
}
Issue | Solution |
---|---|
Mock not injected | Ensure the bean type and qualifier match; use MockitoBean(name="...") if necessary. |
Multiple beans of same type | Disambiguate with @Qualifier or primary bean; mock by name. |
NullPointerException in test | Verify you used @WebMvcTest /@SpringBootTest and autowired collaborators correctly. |
NoSuchBeanDefinition for mock | Declare MockitoBean on the test class (not inside a nested config). |
Stubs not applied | Call stubbing before invoking the SUT; avoid final methods unless using mock-maker-inline. |
@SpyBean
sparingly—prefer pure mocks or pure unit tests.mockito-inline
dependency to mock finals if needed.@Mock
is a Mockito mock created in plain unit tests. MockitoBean
registers the mock as a Spring bean, replacing the real bean in the application context for the test.
MockitoBean
to replace collaborators not included in the slice, or to decouple expensive integrations.
org.mockito:mockito-inline
) to allow mocking final classes/methods.
MockitoBean
is the go-to tool for isolating layers in Spring Boot tests while still benefiting from Spring’s context and autowiring. Use it to replace expensive or external collaborators, keep tests focused, and combine with @SpyBean
when partial mocking is justified.