When creating tests and mocking dependencies, what is the difference between these three approaches?
@MockBean:
@MockBean MyService myservice;
@Mock:
@Mock MyService myservice;
Mockito.mock()
MyService myservice = Mockito.mock(MyService.class);
When creating tests and mocking dependencies, what is the difference between these three approaches?
@MockBean:
@MockBean
MyService myservice;
@Mock:
@Mock
MyService myservice;
Mockito.mock()
MyService myservice = Mockito.mock(MyService.class);
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"));
}
}
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
.
Mocktio.mock() :-
when(..)
and thenReturn(..)
methods for mock objects
whose class methods will be invoked during test case execution.@Mock
:-
mock()
method so it is preferable and often
used.mock()
and @Mock
are functionally equivalent.To enable the Mockito annotation during test execution we need to call the MockitoAnnotations.initMocks(this)
method but this method is deprecated instead we can call - MockitoAnnotations.openMocks(this)
. In order to avoid the side effects it is advised to call this method before test case executions.
Another way to enable the Mockito annotations is by annotating the test class with @RunWith
by specifying the MockitoJUnitRunner that does this task and also other useful things.
@MockBean
:-
It is used to add the mock objects into spring application context.
This mock will replace the existing bean of same type in application
context. If no bean is available, then new bean will be added. This
is useful in integration test case.
When we write a test case that doesn't need any dependencies from the Spring Boot container, the classic/plain Mockito is used and it is fast and favors the isolation of the tested component.
If our test case needs to rely on the Spring Boot container and want to add or mock one of the container beans then @MockBean
is used.
Shortly we can say for @MockBean
annotation: The mock will substitute any existing bean of the same type within the application context, and if no bean of the same type is defined, a new one will be included. This annotation proves valuable during integration tests
when it becomes necessary to mock a specific bean, such as an external service
.