web-dev-qa-db-fra.com

Différence entre @Mock et @InjectMocks

Quelle est la différence entre @Mock et @InjectMocks dans le framework Mockito?

318
user2249972

@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...

}
391
Tom Verelst

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>

89
aldok

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é.

64
user2989087

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.

47
Mik378
  • @Mock crée une implémentation factice pour les classes dont vous avez besoin.
  • @InjectMock crée une instance de la classe et y injecte les simulacres marqués des annotations @Mock.

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.

Ce lien contient un bon tutoriel pour le framework Mockito

14
Sana Jahan

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.

12
serup

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.

6
tintin

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/

2
avp

@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(){

   }

}
1
dev_2014

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"));
    }
1

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

0
user7294900