web-dev-qa-db-fra.com

Quelle est la différence entre moqueur et espionnage lors de l'utilisation de Mockito?

Quel serait un cas d'utilisation pour l'utilisation d'un espion Mockito?

Il me semble que chaque cas d'utilisation d'un espion peut être traité avec une simulation, en utilisant callRealMethod.

Une différence que je peux voir est que si vous voulez que la plupart des appels de méthode soient réels, cela enregistre certaines lignes de code pour utiliser un simulacre contre un espion. Est-ce ou est-ce que je manque la vue d'ensemble?

125
Victor Grazi

La réponse est dans la documentation :

Vraies fausses partielles (depuis le 1.8.0)

Enfin, après de nombreux débats et discussions internes sur la liste de diffusion, un support factice partiel a été ajouté à Mockito. Auparavant, nous considérions les simulacres partiels comme des odeurs de code. Cependant, nous avons trouvé un cas d'utilisation légitime pour des simulacres partiels.

Avant la version 1.8, spy () ne produisait pas de véritables simulacres partiels et prêtait à confusion pour certains utilisateurs. En savoir plus sur l'espionnage: here ou javadoc pour la méthode espion (Object).

callRealMethod() a été introduit après spy(), mais spy () a été laissé là bien sûr pour assurer la compatibilité avec les versions antérieures.

Sinon, vous avez raison: toutes les méthodes d'un espion sont réelles, à moins d'être écrasées. Toutes les méthodes d'une maquette sont écrites à moins que callRealMethod() ne soit appelé. En général, je préférerais utiliser callRealMethod(), car cela ne me force pas à utiliser l'idiome doXxx().when() au lieu du traditionnel when().thenXxx().

90
JB Nizet

Différence entre un espion et un simulacre

Lorsque Mockito crée une maquette, il le fait à partir de la classe d'un type et non d'une instance réelle. Le simulacre crée simplement une instance nue de la classe, entièrement instrumentée pour suivre les interactions avec celle-ci. D'autre part, l'espion va envelopper une instance existante. Il se comportera toujours de la même manière que l'instance normale - la seule différence est qu'il sera également instrumenté pour suivre toutes les interactions avec lui.

Dans l'exemple suivant, nous créons une maquette de la classe ArrayList:

@Test
public void whenCreateMock_thenCreated() {
    List mockedList = Mockito.mock(ArrayList.class);

    mockedList.add("one");
    Mockito.verify(mockedList).add("one");

    assertEquals(0, mockedList.size());
}

Comme vous pouvez le constater, l’ajout d’un élément à la liste simulée n’ajoute rien, il appelle simplement la méthode sans autre effet secondaire. D'autre part, un espion se comportera différemment - il appellera en réalité l'implémentation réelle de la méthode add et ajoutera l'élément à la liste sous-jacente:

@Test
public void whenCreateSpy_thenCreate() {
    List spyList = Mockito.spy(new ArrayList());
    spyList.add("one");
    Mockito.verify(spyList).add("one");

    assertEquals(1, spyList.size());
}

Ici, nous pouvons sûrement dire que la méthode interne réelle de l’objet a été appelée, car lorsque vous appelez la méthode size (), vous obtenez la taille égale à 1, mais cette méthode size () n’a pas été fausse! Alors, d'où vient-il? La méthode interne de la taille réelle () est appelée, car size () n'est pas moqué (ou stubbed) et nous pouvons donc dire que l'entrée a été ajoutée à l'objet réel.

Source: http://www.baeldung.com/mockito-spy + notes personnelles.

75
Saurabh Patil

S'il y a un objet avec 8 méthodes et que vous avez un test où vous voulez appeler 7 méthodes réelles et stub une méthode, vous avez deux options:

  1. En utilisant un simulacre, vous devez le configurer en appelant 7 callRealMethod et la méthode stub one.
  2. En utilisant un spy vous devez le configurer en écrasant une méthode

Le documentation officielle sur doCallRealMethod recommande d'utiliser un espion pour les simulacres partiels.

Voir aussi javadoc spy (Object) pour en savoir plus sur les simulacres partiels. Mockito.spy () est une méthode recommandée pour créer des simulacres partiels. La raison en est que cela garantit que les méthodes réelles sont appelées contre un objet construit correctement, car vous êtes responsable de la construction de l'objet transmis à la méthode spy ().

33
user2412398

Spy peut être utile lorsque vous souhaitez créer des tests unitaires pour le code hérité .

J'ai créé un exemple exécutable ici https://www.surasint.com/mockito-with-spy/ , j'en copie une partie ici.

Si vous avez quelque chose comme ce code:

public void transfer(  DepositMoneyService depositMoneyService, WithdrawMoneyService withdrawMoneyService, 
             double amount, String fromAccount, String toAccount){
    withdrawMoneyService.withdraw(fromAccount,amount);
    depositMoneyService.deposit(toAccount,amount);
}

Vous n'avez peut-être pas besoin d'espionnage car vous pouvez simplement vous moquer de DepositMoneyService et de WithdrawMoneyService.

Mais avec certains, code hérité, la dépendance est dans le code comme ceci:

    public void transfer(String fromAccount, String toAccount, double amount){

        this.depositeMoneyService = new DepositMoneyService();
        this.withdrawMoneyService = new WithdrawMoneyService();

        withdrawMoneyService.withdraw(fromAccount,amount);
        depositeMoneyService.deposit(toAccount,amount);
    }

Oui, vous pouvez modifier le premier code mais l’API est modifiée. Si cette méthode est utilisée par de nombreux endroits, vous devez tous les changer.

Alternative est que vous pouvez extraire la dépendance comme ceci:

    public void transfer(String fromAccount, String toAccount, double amount){
        this.depositeMoneyService = proxyDepositMoneyServiceCreator();
        this.withdrawMoneyService = proxyWithdrawMoneyServiceCreator();

        withdrawMoneyService.withdraw(fromAccount,amount);
        depositeMoneyService.deposit(toAccount,amount);
    }
    DepositMoneyService proxyDepositMoneyServiceCreator() {
        return new DepositMoneyService();
    }

    WithdrawMoneyService proxyWithdrawMoneyServiceCreator() {
        return new WithdrawMoneyService();
    }

Ensuite, vous pouvez utiliser l'espion l'injecter la dépendance comme ceci:

DepositMoneyService mockDepositMoneyService = mock(DepositMoneyService.class);
        WithdrawMoneyService mockWithdrawMoneyService = mock(WithdrawMoneyService.class);

    TransferMoneyService target = spy(new TransferMoneyService());

    doReturn(mockDepositMoneyService)
            .when(target).proxyDepositMoneyServiceCreator();

    doReturn(mockWithdrawMoneyService)
            .when(target).proxyWithdrawMoneyServiceCreator();

Plus de détails dans le lien ci-dessus.

4
Surasin Tancharoen