Quelle est la différence entre @Mock
et @InjectMocks
dans le framework Mockito?
@Mock
crée une maquette. @InjectMocks
crée une instance de la classe et injecte les répliques créées avec les annotations @Mock
(ou @Spy
) dans cette instance.
Notez que vous devez utiliser @RunWith(MockitoJUnitRunner.class)
ou Mockito.initMocks(this)
pour initialiser ces simulacres et les injecter.
@RunWith(MockitoJUnitRunner.class)
public class SomeManagerTest {
@InjectMocks
private SomeManager someManager;
@Mock
private SomeDependency someDependency; // this will be injected into someManager
//tests...
}
Ceci est un exemple de code sur le fonctionnement de @Mock
et @InjectMocks
.
Supposons que nous ayons les classes Game
et 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;
}
}
Comme vous le voyez, la classe Game
a besoin de Player
pour exécuter une 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 va se moquer d'une classe Player et son comportement en utilisant les méthodes when
et thenReturn
. Enfin, en utilisant @InjectMocks
, Mockito mettra cette Player
dans Game
.
Notez que vous n'avez même pas besoin de créer un objet new Game
. Mockito l'injectera pour vous.
// you don't have to do this
Game game = new Game(player);
Nous aurons également le même comportement en utilisant l'annotation @Spy
. Même si le nom de l'attribut est différent.
@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();
}
// ...
En effet, Mockito vérifie le Type Signature
de la classe Game, qui est Player
et List<String>
.
Dans votre classe de test, la classe testée doit être annotée avec @InjectMocks. Ceci indique à Mockito dans quelle classe injecter des imitations:
@InjectMocks
private SomeManager someManager;
À partir de là, nous pouvons spécifier quelles méthodes ou quels objets spécifiques de la classe, dans ce cas SomeManager , seront remplacés par des mocks:
@Mock
private SomeDependency someDependency;
Dans cet exemple, SomeDependency de la classe SomeManager sera simulé.
L'annotation @Mock
se moque de l'objet concerné.
L’annotation @InjectMocks
permet d’injecter dans l’objet sous-jacent les différents mockings créés par @Mock
.
Les deux sont complémentaires.
Par exemple
@Mock
StudentDao studentDao;
@InjectMocks
StudentService service;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
}
Ici, nous avons besoin de la classe dao pour la classe de service. Nous nous en moquons donc et l'injectons dans l'instance de classe de service . De même, dans Spring Framework, tous les beans @Autowired peuvent être simulés par @Mock in jUnits et injectés dans votre bean via @InjectMocks.
La méthode MockitoAnnotations.initMocks (this) initialise ces simulacres et les injecte pour chaque méthode de test, elle doit donc être appelée dans la méthode setUp.
Un "framework moqueur", sur lequel Mockito est basé, est un framework qui vous donne la possibilité de créer des objets Mock (ces anciens objets pourraient être appelés shunts, car ils fonctionnent comme des shunts pour la fonctionnalité de dépendance) En d'autres termes , un objet fictif est utilisé pour imiter l'objet réel dont dépend votre code, vous créez un objet proxy avec le framework moqueur . En utilisant des objets fictifs dans vos tests, vous passez essentiellement des tests unitaires normaux aux tests d'intégration.
Mockito est un framework de test open source pour Java publié sous la licence MIT. Il s'agit d'un "framework moqueur" qui vous permet d'écrire de superbes tests avec une API simple et claire. Il existe de nombreux frameworks moqueurs différents dans l'espace Java, mais il existe essentiellement deux types principaux de framework d'objet fictif, ceux implémentés via un proxy et ceux implémentés via le remappage de classe.
Les infrastructures d'injection de dépendances telles que Spring vous permettent d'injecter vos objets proxy sans modifier le code. L'objet fictif s'attend à ce qu'une certaine méthode soit appelée et renvoie le résultat attendu.
L'annotation @InjectMocks
tente d'instancier l'instance de l'objet à tester et injecte des champs annotés avec @Mock
ou @Spy
dans des champs privés de l'objet à tester.
MockitoAnnotations.initMocks(this)
call, réinitialise l'objet à tester et réinitialise les simulacres, alors n'oubliez pas de l'avoir dans votre annotation @Before
/@BeforeMethod
.
L’un des avantages de l’approche mentionnée par @Tom est qu’il n’est pas nécessaire de créer de constructeur dans SomeManager, ce qui limite le nombre de clients pour l’instancier.
@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...
}
Que ce soit une bonne pratique ou non dépend de la conception de votre application.
Beaucoup de gens ont donné une bonne explication ici sur @Mock
vs @InjectMocks
. J'aime ça, mais je pense que nos tests et notre application devraient être écrits de manière à ne pas nécessiter l'utilisation de @InjectMocks
.
Référence pour des lectures supplémentaires avec des exemples: https://tedvinke.wordpress.com/2014/02/13/mockito-why-you-should-not-use-injectmocks-annotation-to-autowire-fields/
@Mock est utilisé pour déclarer/simuler les références des beans dépendants, tandis que @InjectMocks est utilisé pour simuler le bean pour lequel le test est créé.
Par exemple:
public class A{
public class B b;
public void doSomething(){
}
}
test pour la classe A:
public class TestClassA{
@Mocks
public class B b;
@InjectMocks
public class A a;
@Test
public testDoSomething(){
}
}
Les annotations @InjectMocks peuvent être utilisées pour injecter automatiquement des champs fictifs dans un objet de test.
Dans l'exemple ci-dessous, @InjectMocks a utilisé pour injecter le mock dataMap dans la bibliothèque de données.
@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"));
}
Notez que ce @InjectMocks
est sur le point d'être obsolète
deprecate @InjectMocks et programme de suppression dans Mockito 3/4
et vous pouvez suivre @avp answer and link on:
Pourquoi ne pas utiliser l'annotation InjectMocks dans les champs Autowire