Diferencia entre @Mock y @InjectMocks

¿Cuál es la diferencia entre @Mock y @InjectMocks en Mockito framework?

@Mock crea un simulacro. @InjectMocks crea una instancia de la clase e inyecta los simulacros que se crean con las @Mock (o @Spy ) en esta instancia. Tenga en cuenta que debe usar @RunWith(MockitoJUnitRunner.class) o Mockito.initMocks(this) para inicializar estos Mockito.initMocks(this) e Mockito.initMocks(this) .

 @RunWith(MockitoJUnitRunner.class) public class SomeManagerTest { @InjectMocks private SomeManager someManager; @Mock private SomeDependency someDependency; // this will be injected into someManager //tests... } 

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

 @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 mocks:

 @Mock private SomeDependency someDependency; 

En este ejemplo, se burlará SomeDependency dentro de la clase SomeManager.

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

@InjectMocks anotación @InjectMocks permite inyectar en el objeto subyacente los diferentes @Mock creados por @Mock .

Ambos son complementarios.

Este es un código de muestra sobre cómo funciona @Mock y @InjectMocks .

Digamos que tenemos clases de Game y Player .

 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 clase de Game necesita un Player para 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 su comportamiento utilizando el método de when y luego thenReturn . Por último, usando @InjectMocks Mockito pondrá a ese Player en el Game .

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

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

También obtendremos el mismo comportamiento utilizando la anotación @Spy . Incluso si el nombre del atributo es diferente.

 @RunWith(MockitoJUnitRunner.class) public class GameTest { @Mock Player player; @Spy List 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 opponents; public Game(Player player, List opponents) { this.player = player; this.opponents = opponents; } public int numberOfEnemies() { return opponents.size(); } // ... 

Esto se debe a que Mockito verificará la clase Type Signature del juego, que es Player y List .

Un “marco mocking”, basado en Mockito, es un marco que le da la capacidad de crear objetos simulados (en el pasado, estos objetos podrían denominarse derivaciones, ya que funcionan como derivaciones para la funcionalidad dependiente). En otras palabras, una simulación objeto se usa para imitar el objeto real de donde depende su código, usted crea un objeto proxy con el marco de burla. Al usar objetos simulados en sus pruebas, esencialmente pasará de la prueba de unidad normal a la prueba de integración

Mockito es un marco de prueba de código abierto para Java publicado bajo la licencia MIT, es un “marco de burla”, que le permite escribir pruebas hermosas con API limpia y simple. Hay muchos marcos de burla diferentes en el espacio de Java, sin embargo, hay esencialmente dos tipos principales de marcos de objetos simulados, unos que se implementan a través del proxy y otros que se implementan a través de la reasignación de clases.

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

La anotación @InjectMocks 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) call, restablece el objeto de prueba y reinicializa los simulacros, así que recuerde tener esto en su anotación @Before / @BeforeMethod .

  • @Mock crea una implementación simulada para las clases que necesita.
  • @InjectMock crea una instancia de la clase e inyecta los simuladores que están marcados 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 y lo inyectamos en la instancia de la clase de servicio. De manera similar, en el marco Spring todos los beans @Autowired pueden ser burlados por @Mock en jUnits y ser inyectados en su bean a través de @InjectMocks.

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

Este enlace tiene un buen tutorial para el framework Mockito

Una ventaja que obtienes con el enfoque mencionado por @Tom es que no tienes que crear constructores en SomeManager y, por lo tanto, limitar a los clientes para crear instancias.

 @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í sobre @Mock vs @InjectMocks . Me gusta, pero creo que nuestras pruebas y nuestra aplicación deben escribirse de tal manera que no necesitemos usar @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/