Post

Difference between @Mock and @MockBean

Introduction

Both @Mock and @MockBean create mock objects, but they operate in fundamentally different contexts. @Mock is a pure Mockito annotation that works without Spring, while @MockBean is a Spring Boot annotation that injects a mock into the Spring application context. Choosing the wrong one leads to either a test that is slower than it needs to be or one that does not test what I intended.

To make the distinction concrete, I will use a simple OrderService that depends on an OrderRepository:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Service
public class OrderService {

    private final OrderRepository orderRepository;

    public OrderService(OrderRepository orderRepository) {
        this.orderRepository = orderRepository;
    }

    public Order findById(Long id) {
        return orderRepository.findById(id)
                .orElseThrow(() -> new OrderNotFoundException(id));
    }

    public Order place(Order order) {
        order.setStatus(OrderStatus.PLACED);
        return orderRepository.save(order);
    }
}

@Mock

The @Mock annotation is interchangeable with the Mockito.mock() method. I can mock either a class or an interface. To activate the annotation, I put @ExtendWith(MockitoExtension.class) above the test class, or invoke MockitoAnnotations.openMocks(this) in the setup block. (Note: the older initMocks() method has been deprecated since Mockito 2.x; prefer openMocks() instead.)

The benefit of using annotations is readability, and @Mock also makes failures easier to diagnose – the field name appears in the failure message. Combined with @InjectMocks, it eliminates most manual wiring.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
@ExtendWith(MockitoExtension.class)
class OrderServiceTest {

    @Mock
    private OrderRepository orderRepository;

    @InjectMocks
    private OrderService orderService;

    @Test
    void shouldReturnOrderWhenFound() {
        // given
        Order order = new Order(1L, OrderStatus.PLACED);
        when(orderRepository.findById(1L)).thenReturn(Optional.of(order));

        // when
        Order result = orderService.findById(1L);

        // then
        assertThat(result.getId()).isEqualTo(1L);
        assertThat(result.getStatus()).isEqualTo(OrderStatus.PLACED);
    }

    @Test
    void shouldThrowWhenOrderNotFound() {
        // given
        when(orderRepository.findById(99L)).thenReturn(Optional.empty());

        // when & then
        assertThrows(OrderNotFoundException.class,
                () -> orderService.findById(99L));
    }
}

No Spring context is loaded here. Mockito instantiates OrderService directly, injecting the mock OrderRepository through the constructor. A typical test like this runs in under 50ms.

@MockBean

@MockBean is a Spring Boot annotation (provided by the spring-boot-test module, not core Spring Framework). It replaces an existing bean of the same type in the Spring application context with a Mockito mock, or adds a new one if none of that type exists.

This annotation is useful in integration tests where I want the full Spring wiring – transaction management, AOP proxies, security filters – but need to replace one specific dependency.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
@SpringBootTest
class OrderServiceIntegrationTest {

    @Autowired
    private OrderService orderService;

    @MockBean
    private OrderRepository orderRepository;

    @Test
    void shouldReturnOrderWhenFound() {
        // given
        Order order = new Order(1L, OrderStatus.PLACED);
        when(orderRepository.findById(1L)).thenReturn(Optional.of(order));

        // when
        Order result = orderService.findById(1L);

        // then
        assertThat(result.getId()).isEqualTo(1L);
        assertThat(result.getStatus()).isEqualTo(OrderStatus.PLACED);
    }

    @Test
    void shouldThrowWhenOrderNotFound() {
        // given
        when(orderRepository.findById(99L)).thenReturn(Optional.empty());

        // when & then
        assertThrows(OrderNotFoundException.class,
                () -> orderService.findById(99L));
    }
}

The test logic looks almost identical, but the execution is very different. Spring boots the entire application context, creates OrderService through its normal bean lifecycle, and the @MockBean annotation swaps the real OrderRepository with a mock inside that context. This means Spring’s @Transactional boundaries, validation, and any AOP advice around OrderService are all active.

Keep in mind that @MockBean causes the Spring application context to be reloaded (or a new context to be created) when the set of mocked beans differs between test classes. Context startup alone typically takes 2 to 5 seconds per unique configuration, and in large projects with many different @MockBean combinations this adds up fast. Where possible, I try to keep the same @MockBean configuration across test classes to maximize context caching.

Side-by-Side Comparison

 @Mock@MockBean
LibraryMockito (mockito-junit-jupiter)Spring Boot (spring-boot-test)
Requires@ExtendWith(MockitoExtension.class)@SpringBootTest (or @WebMvcTest, etc.)
Application contextNone – plain JavaFull Spring context loaded
Bean replacementNo context, no beansReplaces or adds a bean in the context
SpeedMillisecondsSeconds (context startup)
Wiring@InjectMocks (constructor injection)Spring’s dependency injection
AOP / TransactionsNot activeActive
Best forUnit testsIntegration tests

When to Use Which

Use @Mock when:

  • I am testing a single class in isolation (unit test)
  • I do not need Spring’s dependency injection, transactions, or AOP
  • I want the fastest possible test execution
  • I am following the London school of unit testing (mock all collaborators)

Use @MockBean when:

  • I need the Spring application context (integration test)
  • I want to verify behavior that depends on Spring features (transactions, caching, security, etc.)
  • I need to replace a specific bean while keeping the rest of the real application wiring
  • I am testing a @RestController with @WebMvcTest and need to mock a service layer bean

A common mistake I have seen is using @MockBean in tests that do not actually need the Spring context. This turns what should be a fast unit test into a slow integration test for no benefit. As a rule of thumb: if the test class does not have @SpringBootTest, @WebMvcTest, @DataJpaTest, or a similar Spring test annotation, I use @Mock.1

Related posts: Schools of Unit Testing, Custom Assertions with AssertJ

  1. Both annotations are available in JUnit 5. @ExtendWith(MockitoExtension.class) comes from the Mockito library (mockito-junit-jupiter), while @ExtendWith(SpringExtension.class) comes from the Spring Test module. @SpringBootTest already includes @ExtendWith(SpringExtension.class) implicitly. ↩︎

This post is licensed under CC BY 4.0 by the author.