Skip to content
Spring Boot sb testing 4 min read

Mocking Service Layers

Every test makes a decision: which collaborators are real and which are fakes? Getting that boundary right is what makes a test fast, focused, and trustworthy. This page lays out the two main mocking mechanisms in Spring Boot — plain Mockito @Mock and Spring’s @MockitoBean — and when to use each.

Two mechanisms, two contexts

The key distinction is whether a Spring context is running.

  • Plain Mockito @Mock creates a mock object in plain Java. No Spring context is involved. You inject it yourself (usually via @InjectMocks). This is for pure unit tests.
  • @MockitoBean registers a Mockito mock inside the Spring context, replacing the real bean of that type. The rest of the context is wired normally. This is for slice and integration tests that boot a context.

Note: @MockitoBean (and @MockitoSpyBean) arrived in Spring Boot 3.4 / Spring Framework 6.2 and replace the now-deprecated @MockBean and @SpyBean. New code should use @MockitoBean.

@MockitoBean vs Mockito @Mock

@Mock (Mockito)@MockitoBean (Spring)
Spring contextNoneRequired (slice / @SpringBootTest)
What it doesCreates a bare mock objectReplaces a bean in the context with a mock
InjectionManual / @InjectMocksSpring autowires it everywhere the bean is used
Enabling@ExtendWith(MockitoExtension.class)Automatic in a Spring test
SpeedFastest (no context)Slower (context starts)
Use inPure unit tests@WebMvcTest, @SpringBootTest slices

@Mock in a unit test

@ExtendWith(MockitoExtension.class)
class OrderServiceTest {
    @Mock OrderRepository repository;
    @InjectMocks OrderService service;   // mock injected by Mockito

    @Test
    void findsOrder() {
        when(repository.findById(1L)).thenReturn(Optional.of(new Order(1L)));
        assertThat(service.find(1L)).isNotNull();
    }
}

@MockitoBean in a slice test

In a @WebMvcTest the service layer is not loaded, so you supply it as a context-level mock.

@WebMvcTest(OrderController.class)
class OrderControllerTest {
    @Autowired MockMvc mockMvc;
    @MockitoBean OrderService service;   // replaces the real bean in the context

    @Test
    void returnsOrder() throws Exception {
        when(service.find(1L)).thenReturn(new Order(1L));
        mockMvc.perform(get("/orders/1")).andExpect(status().isOk());
    }
}

Stubbing repositories

When unit-testing a service, mock its repository and stub the methods the code path uses. Return realistic values — including the empty cases.

when(repository.findById(1L)).thenReturn(Optional.of(order));
when(repository.findById(404L)).thenReturn(Optional.empty());   // the not-found path
when(repository.save(any(Order.class))).thenAnswer(inv -> inv.getArgument(0));

Tip: Stubbing save(...) to echo its argument (thenAnswer(inv -> inv.getArgument(0))) is handy when the code uses the saved entity afterward but you do not care about a generated ID.

Faking external clients

Calls to third-party APIs (RestClient, WebClient, an SDK) must never hit the network in a test. Hide each external call behind your own interface, then mock that interface — your tests stay fast and you do not depend on a remote service being up.

public interface PaymentGateway {
    PaymentResult charge(String cardToken, BigDecimal amount);
}
@Mock PaymentGateway gateway;

when(gateway.charge(eq("tok_visa"), any()))
        .thenReturn(new PaymentResult("CONFIRMED", "ch_123"));

// And assert your code reacts correctly to a declined charge:
when(gateway.charge(eq("tok_declined"), any()))
        .thenReturn(new PaymentResult("DECLINED", null));

For testing the client itself, the @RestClientTest slice provides a MockRestServiceServer to stub HTTP responses without a real server.

Mocks vs stubs vs fakes

“Mock” is often used loosely. The distinctions guide what you assert.

DoubleWhat it isYou typically
StubReturns canned answersAssert your code’s output
MockA stub that also records callsverify(...) an interaction happened
FakeA working lightweight implementation (in-memory repo, H2)Treat it like the real thing
SpyWraps a real object, selectively overriddenOverride one method, keep the rest

Test doubles vs real beans — choosing the boundary

Mock at the edges of what you are testing, and use real beans inside. Over-mocking couples tests to implementation details and lets bugs slip between the seams; under-mocking makes tests slow and flaky.

  • Unit test of a service → mock its repository and external clients; the service itself is real.
  • @WebMvcTest → mock the service (@MockitoBean); the controller, validation, and JSON are real.
  • @DataJpaTest → mock nothing — use the real repository against a real (or embedded) database. See @DataJpaTest.
  • Full integration test → mock as little as possible, ideally only truly external systems; use Testcontainers for the database.

Warning: Resist mocking types you do not own (e.g. a third-party SDK class) directly. Wrap them behind your own interface and mock that. When the library changes, you update one adapter instead of dozens of brittle mock setups.

Last updated June 13, 2026
Was this helpful?