¿Cuál es la diferencia entre @Mock
y @InjectMocks
en el framework Mockito?
@Mock
crea un mock. @InjectMocks
crea una instancia de la clase e inyecta los mocks que se crean con las anotaciones @Mock
(o @Spy
) en esta instancia.
Ten en cuenta que debes utilizar @RunWith(MockitoJUnitRunner.class)
o Mockito.initMocks(this)
para inicializar estos mocks e inyectarlos.
@RunWith(MockitoJUnitRunner.class)
public class SomeManagerTest {
@InjectMocks
private SomeManager someManager;
@Mock
private SomeDependency someDependency; // this will be injected into someManager
//tests...
}
Este es un código de muestra de cómo funciona "Simulacro" e "Inyección de Simulacros".
Digamos que tenemos la clase "Juego" y "Jugador".
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 ves, la clase de "Juego" necesita "Jugador" para realizar un "ataque".
@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 jugadores y de su comportamiento usando el método de "cuándo" y "después de la vuelta". Por último, usando "Inyección de burlas" Mockito pondrá a ese "jugador" en el "juego".
Fíjate que no tienes que crear un objeto "nuevo juego". Mockito lo inyectará por ti.
// you don't have to do this
Game game = new Game(player);
También obtendremos el mismo comportamiento usando la anotación Espía
.
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 revisará la clase "Type Signature" del juego, que es "Jugador" y "Lista".
En su clase de prueba, la clase probada debe ser anotada con InjectMocks
.
Esto le dice a Mockito en qué clase debe inyectarse las burlas:
@InjectMocks
private SomeManager someManager;
A partir de ahí, podemos especificar qué métodos u objetos específicos dentro de la clase, en este caso, SomeManager
, serán sustituidos por burlas:
@Mock
private SomeDependency someDependency;
En este ejemplo, "SomeDependencia" dentro de la clase "SomeManager" será objeto de burla.
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. Así que nos burlamos de ella y la inyectamos en la instancia de la clase de servicio. Del mismo modo, en el marco de la primavera todos los frijoles @Autocableados pueden ser burlados por @Mock en jUnits e inyectados en su frijol a través de @InjectMocks.
El método "MockitoAnnotations.initMocks(this)inicializa estas burlas y las inyecta para cada método de prueba, por lo que necesita ser llamado en el método "setUp()
.
[Este enlace tiene un buen tutorial para el framework Mockito][1]
[1]: https://dzone.com/articles/a-guide-to-mocking-with-mockito
Un "mocking framework", en el que se basa Mockito, es un framework que te da la posibilidad de crear objetos Mock ( en términos antiguos estos objetos podrían llamarse shunts, ya que funcionan como shunts para funcionalidad dependiente ) En otras palabras, un objeto mock se utiliza para imitar el objeto real del que depende tu código, creas un objeto proxy con el framework mocking. Al utilizar objetos simulados en tus pruebas, estás pasando de las pruebas unitarias normales a las pruebas de integración.
Mockito es un marco de pruebas de código abierto para Java publicado bajo la Licencia MIT, es un "marco de trabajo de imitación", que le permite escribir hermosas pruebas con una API limpia y simple. Hay muchos marcos de trabajo de mocking en el espacio de Java, sin embargo hay esencialmente dos tipos principales de marcos de trabajo de objetos mock, los que se implementan a través de proxy y los que se implementan a través de la reasignación de clases.
Los frameworks de inyección de dependencia como Spring te permiten inyectar tus objetos proxy sin modificar ningún código, el objeto mock espera que se llame a un determinado método y devolverá un resultado esperado.
La anotación @InjectMocks
trata de instanciar la instancia del objeto de prueba e inyecta los campos anotados con @Mock
o @Spy
en los campos privados del objeto de prueba.
La llamada MockitoAnnotations.initMocks(this)
restablece el objeto de prueba y reinicializa los mocks, así que recuerda tener esto en tu anotación @Before
/ @BeforeMethod
.
Una ventaja que se obtiene con el enfoque mencionado por @Tom es que no hay que crear ningún constructor en el SomeManager, y por lo tanto se limita a los clientes para instanciarlo.
@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.
Mucha gente ha dado una gran explicación aquí acerca de Mock
vs InjectMocks
.
Me gusta, pero creo que nuestras pruebas y aplicaciones deben ser escritas de tal manera que no necesitemos usar "InjectMocks".
Referencia para lectura adicional con ejemplos: https://tedvinke.wordpress.com/2014/02/13/mockito-why-you-should-not-use-injectmocks-annotation-to-autowire-fields/
Mock
se utiliza para declarar/modificar las referencias de los frijoles dependientes, mientras que InjectMocks
se utiliza para simular el frijol para el que se está creando la prueba.
Por ejemplo:
public class A{
public class B b;
public void doSomething(){
}
}
prueba para la clase "A":
public class TestClassA{
@Mocks
public class B b;
@InjectMocks
public class A a;
@Test
public testDoSomething(){
}
}
La anotación @InjectMocks puede ser usada para inyectar campos simulados en un objeto de prueba automáticamente.
En el siguiente ejemplo @InjectMocks se ha utilizado para inyectar el mapa de datos simulado en la 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"));
}
Fíjate que los ``InjectMocks'' están a punto de ser desaprobados
depreciar @InjectMocks y programar la eliminación en Mockito 3/4
y puedes seguir @avp respuesta y enlace en:
Por qué no debes usar la anotación de InjectMocks para autocablear los campos