¿Cuál es la diferencia entre @Mock
y @InjectMocks
en el marco de Mockito?
¿Cuál es la diferencia entre @Mock
y @InjectMocks
en el marco de Mockito?
Respuestas:
@Mock
crea un simulacro. @InjectMocks
crea 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...
}
Este es un código de muestra sobre cómo @Mock
y cómo @InjectMocks
funciona.
Digamos que tenemos Game
y Player
clase.
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 Game
clase necesita Player
realizar 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 when
y thenReturn
método. Por último, el uso de @InjectMocks
Mockito pondrá de que Player
en Game
.
Tenga en cuenta que ni siquiera tiene que crear un new Game
objeto. Mockito lo inyectará por ti.
// you don't have to do this
Game game = new Game(player);
También obtendremos el mismo comportamiento usando @Spy
anotaciones. 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 Signature
clase de Juego, que es Player
y List<String>
.
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, SomeDependency
dentro de la SomeManager
clase se burlarán.
@Mock
La anotación se burla del objeto en cuestión.
@InjectMocks
La anotación permite inyectar en el objeto subyacente los diferentes (y relevantes) simulacros creados por @Mock
.
Ambos son complementarios.
@InjectMocks
para construir esta clase y espiarla también.
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
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 @InjectMocks
anotación intenta crear una instancia de la instancia del objeto de prueba e inyecta campos anotados con @Mock
o @Spy
en 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
/ @BeforeMethod
anotación.
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.
Muchas personas han dado una gran explicación aquí sobre @Mock
vs @InjectMocks
. Me gusta, pero creo que nuestras pruebas y aplicaciones deberían estar escritas de tal manera que no debamos usarlas @InjectMocks
.
Referencia para lecturas adicionales con ejemplos: https://tedvinke.wordpress.com/2014/02/13/mockito-why-you-should-not-use-injectmocks-annotation-to-autowire-fields/
@Mock
se usa para declarar / burlarse de las referencias de los beans dependientes, mientras que @InjectMocks
se 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(){
}
}
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"));
}
Tenga en cuenta que @InjectMocks
está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
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).
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>
}
}