Diferencia entre @Mock y @InjectMocks


Respuestas:


542

@Mockcrea un simulacro. @InjectMockscrea una instancia de la clase e inyecta las simulaciones que se crean con las anotaciones @Mock(o @Spy) en esta instancia.

Tenga en cuenta que debe usar @RunWith(MockitoJUnitRunner.class)o Mockito.initMocks(this)para inicializar estos simulacros e inyectarlos.

@RunWith(MockitoJUnitRunner.class)
public class SomeManagerTest {

    @InjectMocks
    private SomeManager someManager;

    @Mock
    private SomeDependency someDependency; // this will be injected into someManager

     //tests...

}

2
Respuesta breve y concisa. Útil también;)
Chaklader Asfak Arefe

¿Funciona esto para dependencias transitivas o solo para miembros directos?
Pierre Thibault

@PierreThibault Injecting simulacros solo funciona para miembros directos, pero puede configurar un simulacro para permitir stubs profundos static.javadoc.io/org.mockito/mockito-core/3.0.0/org/mockito/…
Tom Verelst

1
Siento que esto es mucho más clara que la mayor parte del artículo en línea .... que pequeños comentarios salvar mi culo ...
IHC_Applroid

Tengo algunos elementos que no pueden proporcionar la anotación @Mock como el contexto. ¿Cómo puedo proporcionar eso para la clase principal?
Mahdi

220

Este es un código de muestra sobre cómo @Mocky cómo @InjectMocksfunciona.

Digamos que tenemos Gamey Playerclase.

class Game {

    private Player player;

    public Game(Player player) {
        this.player = player;
    }

    public String attack() {
        return "Player attack with: " + player.getWeapon();
    }

}

class Player {

    private String weapon;

    public Player(String weapon) {
        this.weapon = weapon;
    }

    String getWeapon() {
        return weapon;
    }
}

Como puede ver, la Gameclase necesita Playerrealizar un attack.

@RunWith(MockitoJUnitRunner.class)
class GameTest {

    @Mock
    Player player;

    @InjectMocks
    Game game;

    @Test
    public void attackWithSwordTest() throws Exception {
        Mockito.when(player.getWeapon()).thenReturn("Sword");

        assertEquals("Player attack with: Sword", game.attack());
    }

}

Mockito se burlará de una clase de jugador y es el comportamiento utilizando wheny thenReturnmétodo. Por último, el uso de @InjectMocksMockito pondrá de que Playeren Game.

Tenga en cuenta que ni siquiera tiene que crear un new Gameobjeto. Mockito lo inyectará por ti.

// you don't have to do this
Game game = new Game(player);

También obtendremos el mismo comportamiento usando @Spyanotaciones. Incluso si el nombre del atributo es diferente.

@RunWith(MockitoJUnitRunner.class)
public class GameTest {

  @Mock Player player;

  @Spy List<String> enemies = new ArrayList<>();

  @InjectMocks Game game;

  @Test public void attackWithSwordTest() throws Exception {
    Mockito.when(player.getWeapon()).thenReturn("Sword");

    enemies.add("Dragon");
    enemies.add("Orc");

    assertEquals(2, game.numberOfEnemies());

    assertEquals("Player attack with: Sword", game.attack());
  }
}

class Game {

  private Player player;

  private List<String> opponents;

  public Game(Player player, List<String> opponents) {
    this.player = player;
    this.opponents = opponents;
  }

  public int numberOfEnemies() {
    return opponents.size();
  }

  // ...

Eso es porque Mockito verificará la Type Signatureclase de Juego, que es Playery List<String>.


16
Con este ejemplo, debería ser la respuesta aceptada.
AnnaKlein

44
Creo que esta es la mejor respuesta también
Evgeniy Dorofeev

44
Creo que esta es la mejor respuesta triple.
Harvey Dent

1
A veces encuentro las pruebas con burlas difíciles de entender y diseñar para una clase. Sin embargo, este ejemplo ayuda mucho a proporcionar una visión general.
Chaklader Asfak Arefe

1
Muchas gracias :) Hasta el punto con una mejor explicación.
Rishi

80

En su clase de prueba, la clase probada debe ser anotada con @InjectMocks. Esto le dice a Mockito en qué clase inyectar simulacros:

@InjectMocks
private SomeManager someManager;

A partir de ese momento, podemos especificar qué métodos u objetos específicos dentro de la clase, en este caso SomeManager, serán sustituidos por simulacros:

@Mock
private SomeDependency someDependency;

En este ejemplo, SomeDependencydentro de la SomeManagerclase se burlarán.


66
¿funcionará esto si someManager tiene más de un constructor? ¿Qué pasaría si someManager tuviera 5 constructores? ¿Cómo sabría cuál desea utilizar?
j2emanue

51

@Mock La anotación se burla del objeto en cuestión.

@InjectMocksLa anotación permite inyectar en el objeto subyacente los diferentes (y relevantes) simulacros creados por @Mock.

Ambos son complementarios.


1
¿Se pueden usar en tándem en el mismo objeto?
IgorGanapolsky

1
¿Tiene un mini-ejemplo de su requerimiento?
Mik378

Tengo una clase que debe ser espiada (a través de Mockito Spy), y esta clase tiene un constructor. Así que estaba pensando en usar @InjectMockspara construir esta clase y espiarla también.
IgorGanapolsky

1
¿Es eso lo que estás buscando? stackoverflow.com/a/35969166/985949
Mik378

23
  • @Mock crea una implementación simulada para las clases que necesita.
  • @InjectMock crea una instancia de la clase e inyecta las simulaciones marcadas con las anotaciones @Mock en ella.

Por ejemplo

@Mock
StudentDao studentDao;

@InjectMocks
StudentService service;

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

Aquí necesitamos la clase DAO para la clase de servicio. Entonces, nos burlamos de él y lo inyectamos en la instancia de la clase de servicio. De manera similar, en Spring Framework todos los beans @Autowired pueden ser burlados por @Mock en jUnits e inyectados en su bean a través de @InjectMocks.

MockitoAnnotations.initMocks(this)El método inicializa estos simulacros y los inyecta para cada método de prueba, por lo que debe invocarse en el setUp()método.

Este enlace tiene un buen tutorial para el framework Mockito


13

Un "marco de simulación", en el que se basa Mockito, es un marco que le brinda la capacidad de crear objetos simulados (en términos antiguos, estos objetos podrían llamarse derivaciones, ya que funcionan como derivaciones para la funcionalidad dependiente) En otras palabras, una simulación El objeto se usa para imitar el objeto real del que depende su código; usted crea un objeto proxy con el marco de imitación. Al usar objetos simulados en sus pruebas, esencialmente pasará de las pruebas unitarias normales a las pruebas integrales

Mockito es un marco de prueba de código abierto para Java lanzado bajo la Licencia MIT, es un "marco de prueba", que le permite escribir hermosas pruebas con API limpia y simple. Hay muchos marcos de simulación diferentes en el espacio Java, sin embargo, existen esencialmente dos tipos principales de marcos de objeto simulados, los que se implementan a través de proxy y los que se implementan a través de la reasignación de clases.

Los marcos de inyección de dependencia como Spring le permiten inyectar sus objetos proxy sin modificar ningún código, el objeto simulado espera que se invoque un cierto método y devolverá un resultado esperado.

La @InjectMocksanotación intenta crear una instancia de la instancia del objeto de prueba e inyecta campos anotados con @Mocko @Spyen campos privados del objeto de prueba.

MockitoAnnotations.initMocks(this)llamar, restablece el objeto de prueba y reinicia los simulacros, así que recuerde tener esto en su @Before/ @BeforeMethodanotación.


2
No diría que "Al usar objetos simulados en sus pruebas, esencialmente pasará de la prueba de unidad normal a la prueba de integración". Para mí, burlarse es aislar el dispositivo que se va a probar para realizar una prueba unitaria. Las pruebas de integración utilizarán dependencias reales no simuladas.
WesternGun

10

Una ventaja que obtiene con el enfoque mencionado por @Tom es que no tiene que crear ningún constructor en SomeManager y, por lo tanto, limita a los clientes a crear una instancia.

@RunWith(MockitoJUnitRunner.class)
public class SomeManagerTest {

    @InjectMocks
    private SomeManager someManager;

    @Mock
    private SomeDependency someDependency; // this will be injected into someManager

    //You don't need to instantiate the SomeManager with default contructor at all
   //SomeManager someManager = new SomeManager();    
   //Or SomeManager someManager = new SomeManager(someDependency);

     //tests...

}

Si es una buena práctica o no, depende del diseño de su aplicación.


¿Qué pasaría si someManager tuviera 3 constructores diferentes, cómo sabría cuál usar?
j2emanue

¿Cómo se verifican las cosas en someManager si no se burla?
IgorGanapolsky


4

@Mockse usa para declarar / burlarse de las referencias de los beans dependientes, mientras que @InjectMocksse usa para burlarse del bean para el que se está creando la prueba.

Por ejemplo:

public class A{

   public class B b;

   public void doSomething(){

   }

}

prueba para clase A:

public class TestClassA{

   @Mocks
   public class B b;

   @InjectMocks
   public class A a;

   @Test
   public testDoSomething(){

   }

}

4

La anotación @InjectMocks se puede usar para inyectar campos simulados en un objeto de prueba automáticamente.

En el ejemplo a continuación, @InjectMocks ha utilizado para inyectar el dataMap simulado en dataLibrary.

@Mock
Map<String, String> dataMap ;

@InjectMocks
DataLibrary dataLibrary = new DataLibrary();


    @Test
    public void whenUseInjectMocksAnnotation_() {
        Mockito.when(dataMap .get("aData")).thenReturn("aMeaning");

        assertEquals("aMeaning", dataLibrary .getMeaning("aData"));
    }

3

Tenga en cuenta que @InjectMocksestán a punto de quedar en desuso

desaprobar @InjectMocks y programar la eliminación en Mockito 3/4

y puedes seguir la respuesta y el enlace de @avp en:

Por qué no debe usar la anotación InjectMocks para los campos de cableado automático


3

Aunque las respuestas anteriores han cubierto, acabo de intentar agregar detalles minuciosos que veo que faltan. La razón detrás de ellos (The Why).

ingrese la descripción de la imagen aquí


Ilustración:

Sample.java
---------------
    public class Sample{
        DependencyOne dependencyOne;
        DependencyTwo dependencyTwo;


        public SampleResponse methodOfSample(){
            dependencyOne.methodOne();
            dependencyTwo.methodTwo();

            ...

            return sampleResponse;
        }
    }

SampleTest.java
-----------------------
@RunWith(PowerMockRunner.class)
@PrepareForTest({ClassA.class})
public class SampleTest{

    @InjectMocks
    Sample sample;

    @Mock
    DependencyOne dependencyOne;

    @Mock
    DependencyTwo dependencyTwo;

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

    public void sampleMethod1_Test(){
        //Arrange the dependencies
        DependencyResponse dependencyOneResponse = Mock(sampleResponse.class);
        Mockito.doReturn(dependencyOneResponse).when(dependencyOne).methodOne();

        DependencyResponse dependencyTwoResponse = Mock(sampleResponse.class);
        Mockito.doReturn(dependencyOneResponse).when(dependencyTwo).methodTwo();

        //call the method to be tested
        SampleResponse sampleResponse = sample.methodOfSample() 

        //Assert
        <assert the SampleResponse here>
    }
}

Referencia

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.