Mockito - Je comprends qu'un espion appelle les méthodes réelles sur un objet, tandis qu'un simulacre appelle des méthodes sur l'objet double. Les espions doivent également être évités à moins qu'il n'y ait une odeur de code. Cependant, comment fonctionnent les espions et quand dois-je les utiliser? En quoi sont-ils différents des simulacres?
99
Réponses:
Techniquement parlant, les «mocks» et les «espions» sont un type spécial de «tests doubles».
Mockito rend malheureusement la distinction bizarre.
Une maquette dans mockito est une maquette normale dans d'autres frameworks moqueurs (vous permet de stuber les appels , c'est-à-dire de renvoyer des valeurs spécifiques hors des appels de méthode).
Un espion dans mockito est une simulation partielle dans d'autres frameworks moqueurs (une partie de l'objet sera moquée et une partie utilisera des invocations de méthodes réelles).
la source
Les deux peuvent être utilisés pour simuler des méthodes ou des champs. La différence est que dans le simulacre, vous créez un objet simulé ou faux complet tandis que dans l'espionnage, il y a l'objet réel et vous ne faites qu'espionner ou en stuber des méthodes spécifiques.
Dans les objets espions, bien sûr, puisqu'il s'agit d'une méthode réelle, lorsque vous ne stubbing pas la méthode, elle appellera le comportement réel de la méthode. Si vous souhaitez modifier et vous moquer de la méthode, vous devez la stuber.
Considérez l'exemple ci-dessous à titre de comparaison.
import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.Spy; import org.mockito.runners.MockitoJUnitRunner; import java.util.ArrayList; import java.util.List; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.when; @RunWith(MockitoJUnitRunner.class) public class MockSpy { @Mock private List<String> mockList; @Spy private List<String> spyList = new ArrayList(); @Test public void testMockList() { //by default, calling the methods of mock object will do nothing mockList.add("test"); Mockito.verify(mockList).add("test"); assertEquals(0, mockList.size()); assertNull(mockList.get(0)); } @Test public void testSpyList() { //spy object will call the real method when not stub spyList.add("test"); Mockito.verify(spyList).add("test"); assertEquals(1, spyList.size()); assertEquals("test", spyList.get(0)); } @Test public void testMockWithStub() { //try stubbing a method String expected = "Mock 100"; when(mockList.get(100)).thenReturn(expected); assertEquals(expected, mockList.get(100)); } @Test public void testSpyWithStub() { //stubbing a spy method will result the same as the mock object String expected = "Spy 100"; //take note of using doReturn instead of when doReturn(expected).when(spyList).get(100); assertEquals(expected, spyList.get(100)); } }
Quand devriez-vous utiliser la simulation ou l'espionnage? Si vous voulez être en sécurité et éviter d'appeler des services externes et que vous voulez simplement tester la logique à l'intérieur de l'unité, utilisez mock. Si vous souhaitez appeler un service externe et effectuer un appel de dépendance réelle, ou simplement dire que vous souhaitez exécuter le programme tel quel et simplement des méthodes spécifiques au stub, utilisez spy. Voilà donc la différence entre espionner et simuler dans mockito.
la source
TL; version DR,
Avec simulacre , il crée une instance de coque nue pour vous.
Avec spy, vous pouvez vous moquer partiellement d'une instance existante
List<String> spyList = Mockito.spy(new ArrayList<String>());
Cas d'utilisation typique pour Spy: la classe a un constructeur paramétré, vous voulez d'abord créer l'objet.
la source
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 WithdrawMoneyService.
Mais avec du 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 passer au premier code, mais l'API est ensuite modifiée. Si cette méthode est utilisée par de nombreux endroits, vous devez tous les changer.
L'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 pour 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.
la source
Le meilleur endroit pour commencer est probablement la documentation pour mockito .
D'une manière générale, la maquette mockito vous permet de créer des talons.
Vous créeriez une méthode stub si, par exemple, cette méthode effectue une opération coûteuse. Disons qu'il obtient une connexion à la base de données, récupère une valeur de la base de données et la renvoie à l'appelant. L'obtention de la connexion à la base de données peut prendre 30 secondes, ralentissant l'exécution de votre test au point où vous basculerez probablement de contexte (ou arrêterez d'exécuter le test).
Si la logique que vous testez ne se soucie pas de la connexion à la base de données, vous pouvez remplacer cette méthode par un stub qui renvoie une valeur codée en dur.
L'espion mockito vous permet de vérifier si une méthode appelle d'autres méthodes. Cela peut être très utile lorsque vous essayez de tester le code hérité.
Il est utile si vous testez une méthode qui fonctionne à travers les effets secondaires, alors vous utiliseriez un espion mockito. Cela délègue les appels à l'objet réel et vous permet de vérifier l'appel de méthode, le nombre de fois invoqué, etc.
la source
En bref:
@Spy
et@Mock
sont largement utilisés dans les tests de code, mais les développeurs ne savent pas quand utiliser l'un d'eux et donc les développeurs finissent par utiliser@Mock
pour être sûrs.@Mock
lorsque vous souhaitez simplement tester la fonctionnalité en externe sans appeler cette méthode.@Spy
lorsque vous souhaitez tester la fonctionnalité en externe + en interne avec la méthode même appelée.Voici l'exemple où j'ai pris le scénario d' Election20xx en Amérique.
Les électeurs peuvent être divisés selon
VotersOfBelow21
etVotersOfABove21
.Le sondage de sortie idéal dit que Trump gagnera les élections parce
VotersOfBelow21
que lesVotersOfABove21
deux voteront pour Trump en disant " Nous avons élu le président Trump "Mais ce n'est pas le vrai scénario:
Alors, comment le tester?
public class VotersOfAbove21 { public void weElected(String myVote){ System.out.println("Voters of above 21 has no Choice Than Thrump in 20XX "); } }
public class VotersOfBelow21 { public void weElected(String myVote){ System.out.println("Voters of below 21 has no Choice Than Thrump in 20XX"); } }
public class ElectionOfYear20XX { VotersOfAbove21 votersOfAbove21; VotersOfBelow21 votersOfBelow21; public boolean weElected(String WeElectedTrump){ votersOfAbove21.weElected(WeElectedTrump); System.out.println("We elected President Trump "); votersOfBelow21.weElected(WeElectedTrump); System.out.println("We elected President Trump "); return true; } }
Notez maintenant que dans les deux premières classes ci-dessus, les deux personnes du groupe d'âge disent qu'elles n'ont pas de meilleur choix que Trump. Ce qui signifie explicitement qu'ils ont voté pour Trump simplement parce qu'ils n'avaient pas le choix.
Maintenant, le
ElectionOfYear20XX
dit que Trump a gagné parce que les deux groupes d'âge ont voté pour lui à une écrasante majorité.Si nous devions tester le
ElectionOfYear20XX
avec @Mock, alors nous ne serions peut-être pas en mesure d'obtenir la vraie raison pour laquelle Trump a gagné, nous allons simplement tester la raison externe.Si nous testons le
ElectionOfYear20XX
avec @Spy, alors nous obtenons la vraie raison pour laquelle Trump a gagné avec les résultats du sondage de sortie externe, c'est-à-dire en interne + en externe.Notre
ELectionOfYear20XX_Test
classe:@RunWith(MockitoJUnitRunner.class) public class ELectionOfYear20XX_Test { @Mock VotersOfBelow21 votersOfBelow21; @Mock VotersOfAbove21 votersOfAbove21; @InjectMocks ElectionOfYear20XX electionOfYear20XX; @Test public void testElectionResults(){ Assert.assertEquals(true,electionOfYear20XX.weElected("No Choice")); } }
Cela devrait produire uniquement les résultats du test logique, c'est-à-dire une vérification externe:
Test en
@Spy
externe et en interne avec un appel de méthode réel.@RunWith(MockitoJUnitRunner.class) public class ELectionOfYear20XX_Test { @Spy VotersOfBelow21 votersOfBelow21; @Spy VotersOfAbove21 votersOfAbove21; @InjectMocks ElectionOfYear20XX electionOfYear20XX; @Test public void testElectionResults(){ Assert.assertEquals(true,electionOfYear20XX.weElected("No Choice")); } }
Production:
Voters of above 21 has no Choice Than Thrump in 20XX We elected President Trump Voters of below 21 has no Choice Than Thrump in 20XX We elected President Trump
la source
J'aime la simplicité de cette recommandation:
Source: https://javapointers.com/tutorial/difference-between-spy-and-mock-in-mockito/
Une différence commune est:
la source