Diferencia entre @Mock, @MockBean y Mockito.mock ()

147

Al crear pruebas y burlarse de dependencias, ¿cuál es la diferencia entre estos tres enfoques?

  1. @MockBean:

    @MockBean
    MyService myservice;
  2. @Burlarse de:

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

    MyService myservice = Mockito.mock(MyService.class);
Doug
fuente

Respuestas:

198

Biblioteca Mockito simple

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

y

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

provienen de la biblioteca Mockito y son funcionalmente equivalentes.
Permiten burlarse de una clase o una interfaz y registrar y verificar comportamientos en ella.

La forma de usar la anotación es más corta, por lo que es preferible y, a menudo, preferida.


Tenga en cuenta que para habilitar las anotaciones de Mockito durante las ejecuciones de prueba, MockitoAnnotations.initMocks(this)se debe llamar al método estático.
Para evitar efectos secundarios entre las pruebas, se recomienda hacerlo antes de cada ejecución de prueba:

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

Otra forma de habilitar las anotaciones de Mockito es anotar la clase de prueba @RunWithespecificando MockitoJUnitRunnerqué hace esta tarea y también otras cosas útiles:

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

Biblioteca Spring Boot que envuelve la biblioteca Mockito

Esta es de hecho una clase Spring Boot :

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

La clase está incluida en la spring-boot-testbiblioteca.

Permite agregar simulacros de Mockito en un Spring ApplicationContext.
Si existe un bean compatible con la clase declarada en el contexto, lo reemplaza por el simulacro.
Si no es el caso, agrega el simulacro en el contexto como un bean.

Referencia de Javadoc:

Anotación que se puede usar para agregar simulacros a un Spring ApplicationContext.

...

Si cualquier bean único existente del mismo tipo definido en el contexto será reemplazado por el simulacro, si no se define ningún bean existente, se agregará uno nuevo.


¿Cuándo se usa Mockito clásico / simple y cuando se usa @MockBeandesde Spring Boot?

Las pruebas unitarias están diseñadas para probar un componente de forma aislada de otros componentes y las pruebas unitarias también tienen un requisito: ser lo más rápido posible en términos de tiempo de ejecución ya que estas pruebas pueden ejecutarse cada día docenas de veces en las máquinas desarrolladoras.

En consecuencia, aquí hay una guía simple:

A medida que escribe una prueba que no necesita dependencias del contenedor Spring Boot, el clásico / simple Mockito es el camino a seguir: es rápido y favorece el aislamiento del componente probado.
Si su prueba necesita basarse en el contenedor Spring Boot y también desea agregar o burlarse de uno de los beans del contenedor: @MockBeandesde Spring Boot es el camino.


Uso típico de Spring Boot @MockBean

Mientras escribimos una clase de prueba anotada con @WebMvcTest(segmento de prueba web).

La documentación de Spring Boot lo resume muy bien:

A menudo @WebMvcTestse limitará a un solo controlador y se usará en combinación @MockBeanpara proporcionar implementaciones simuladas para los colaboradores requeridos.

Aquí hay un ejemplo :

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"));
    }

}
davidxxx
fuente
44
¿Usar @MockBean creará una copia del bean e inyectará en el ApplicationContext? ¿O tendrá el bean burlado todos sus métodos como nulos? Si todos los métodos son nulos, ¿puedo tropezarlos como puedo hacer con @Mock?
Doug
66
Como se explicó, el uso @MockBeanreemplazará el bean en el contexto de la aplicación si un bean que declara el mismo tipo ya está definido en su configuración Spring. Y la inyección se realiza en la clase donde declaras. @MockBean.Los mecanismos DI funcionan de esta manera: registras un objeto en el contexto DI y luego puedes inyectar el objeto referenciado en el contexto Spring en una clase específica. No inyecta un objeto en el contexto DI.
davidxxx
13

Al final es fácil de explicar. Si solo observa los javadocs de las anotaciones, verá los diferentes:

@Mock: ( org.mockito.Mock)

Marcar un campo como un simulacro.

  • Permite la creación de simulacros de taquigrafía.
  • Minimiza el código repetitivo de creación simulada.
  • Hace que la clase de prueba sea más legible.
  • Hace que el error de verificación sea más fácil de leer porque el nombre del campo se usa para identificar el simulacro.

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

Anotación que se puede usar para agregar simulacros a un Spring ApplicationContext. Se puede usar como una anotación de nivel de clase o en campos en cualquiera de las @Configurationclases o en las clases de prueba que son @RunWithSpringRunner.

Los simulacros se pueden registrar por tipo o por nombre de bean. Cualquier simulacro existente del mismo tipo definido en el contexto será reemplazado por el simulacro, si no se define ningún legumbre existente, se agregará uno nuevo.

Cuando @MockBeanse usa en un campo, además de estar registrado en el contexto de la aplicación, el simulacro también se inyectará en el campo.

Mockito.mock ()

Es solo la representación de a @Mock.

Patricio
fuente
55
No olvidemos que @Mock requiere que MockitoRunner o initMocks se llamen manualmente.
Florian Schaetz
44
¿Es la única diferencia entre @MockBeany @Mockque uno inyectará el simulacro en Spring ApplicationContexty el otro no?
Doug
3
@Doug Lo resumiste bien, pero hay que recordar que MockBean es parte de Spring Boot
presentador el