Difference between @Mock, @MockBean and Mockito.mock()

112,136

Solution 1

Plain Mockito library

import org.mockito.Mock;
...
@Mock
MyService myservice;

and

import org.mockito.Mockito;
...
MyService myservice = Mockito.mock(MyService.class);

come from the Mockito library and are functionally equivalent.
They allow to mock a class or an interface and to record and verify behaviors on it.

The way using annotation is shorter, so preferable and often preferred.


Note that to enable Mockito annotations during test executions, the MockitoAnnotations.initMocks(this) static method has to be called.
To avoid side effect between tests, it is advised to do it before each test execution :

@Before 
public void initMocks() {
    MockitoAnnotations.initMocks(this);
}

Another way to enable Mockito annotations is annotating the test class with @RunWith by specifying the MockitoJUnitRunner that does this task and also other useful things :

@RunWith(org.mockito.runners.MockitoJUnitRunner.class)
public MyClassTest{...}

Spring Boot library wrapping Mockito library

This is indeed a Spring Boot class:

import org.springframework.boot.test.mock.mockito.MockBean;
...
@MockBean
MyService myservice;

The class is included in the spring-boot-test library.

It allows to add Mockito mocks in a Spring ApplicationContext.
If a bean, compatible with the declared class exists in the context, it replaces it by the mock.
If it is not the case, it adds the mock in the context as a bean.

Javadoc reference :

Annotation that can be used to add mocks to a Spring ApplicationContext.

...

If any existing single bean of the same type defined in the context will be replaced by the mock, if no existing bean is defined a new one will be added.


When use classic/plain Mockito and when use @MockBean from Spring Boot ?

Unit tests are designed to test a component in isolation from other components and unit tests have also a requirement : being as fast as possible in terms of execution time as these tests may be executed each day dozen times on the developer machines.

Consequently, here is a simple guideline :

As you write a test that doesn't need any dependencies from the Spring Boot container, the classic/plain Mockito is the way to follow : it is fast and favors the isolation of the tested component.
If your test needs to rely on the Spring Boot container and you want also to add or mock one of the container beans : @MockBean from Spring Boot is the way.


Typical usage of Spring Boot @MockBean

As we write a test class annotated with @WebMvcTest (web test slice).

The Spring Boot documentation summarizes that very well :

Often @WebMvcTest will be limited to a single controller and used in combination with @MockBean to provide mock implementations for required collaborators.

Here is an example :

import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.http.MediaType;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;

import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;

@RunWith(SpringRunner.class)
@WebMvcTest(FooController.class)
public class FooControllerTest {

    @Autowired
    private MockMvc mvc;

    @MockBean
    private FooService fooServiceMock;

    @Test
    public void testExample() throws Exception {
         Foo mockedFoo = new Foo("one", "two");

         Mockito.when(fooServiceMock.get(1))
                .thenReturn(mockedFoo);

         mvc.perform(get("foos/1")
            .accept(MediaType.TEXT_PLAIN))
            .andExpect(status().isOk())
            .andExpect(content().string("one two"));
    }

}

Solution 2

At the end its easy to explain. If you just look into the javadocs of the annotations you will see the differences:

@Mock: (org.mockito.Mock)

Mark a field as a mock.

  • Allows shorthand mock creation.
  • Minimizes repetitive mock creation code.
  • Makes the test class more readable.
  • Makes the verification error easier to read because the field name is used to identify the mock.

@MockBean: (org.springframework.boot.test.mock.mockito.MockBean)

Annotation that can be used to add mocks to a Spring ApplicationContext. Can be used as a class level annotation or on fields in either @Configuration classes, or test classes that are @RunWith the SpringRunner.

Mocks can be registered by type or by bean name. Any existing single bean of the same type defined in the context will be replaced by the mock, if no existing bean is defined a new one will be added.

When @MockBean is used on a field, as well as being registered in the application context, the mock will also be injected into the field.

Mockito.mock()

Its just the representation of a @Mock.

Share:
112,136

Related videos on Youtube

Doug
Author by

Doug

Software engineer with extensive experience working across the full development stack (backend, frontend, DevOps). Passionated about JavaScript, React and functional paradigm.

Updated on April 22, 2022

Comments

  • Doug
    Doug about 2 years

    When creating tests and mocking dependencies, what is the difference between these three approaches?

    1. @MockBean:

      @MockBean
      MyService myservice;
      
    2. @Mock:

      @Mock
      MyService myservice;
      
    3. Mockito.mock()

      MyService myservice = Mockito.mock(MyService.class);
      
  • Florian Schaetz
    Florian Schaetz almost 7 years
    Let's not forget that @Mock requires the MockitoRunner or initMocks being called manually.
  • Doug
    Doug almost 7 years
    Will using @MockBean create a copy of the bean and inject it to the ApplicationContext? Or will the mocked bean have all its methods as null? If all the methods are null can I stub them like I can do using @Mock?
  • Doug
    Doug almost 7 years
    Is the only difference between @MockBean and @Mock that one will inject the mock into the Spring ApplicationContext and the other won't?
  • davidxxx
    davidxxx almost 7 years
    As explained, using @MockBean will replace the bean in the application context if a bean declaring the same type is already defined in your Spring configuration. And the injection is performed in the class where you declare @MockBean. The DI mechanisms works in this way : you register an object in the DI context and then you can inject the object referenced in the Spring context in a specific class. You don't inject an object in the DI context.
  • comiventor
    comiventor over 5 years
    @Doug You summarized it well but one needs to remember that MockBean is part of Spring Boot
  • Jaison Varghese
    Jaison Varghese over 3 years
    To use @MockBean you would need to annotate the class with @RunWith(SpringRunner.class). However for using @Mock you can use @RunWith(MockitoJUnitRunner.class) and call initMocks() as @Florian-schaetz mentioned. @Mock will work with SpringRunner as well but with the added overhead of loading the applicationContext
  • JayC
    JayC over 2 years
    I am following but how come the service class have 0 % coverage?