Mockito: entiendo que un espía llama a los métodos reales en un objeto, mientras que un simulacro llama a los métodos en el objeto doble. También se deben evitar los espías a menos que haya un olor a código. Sin embargo, ¿cómo funcionan los espías y cuándo debería utilizarlos? ¿En qué se diferencian de las burlas?
99
Respuestas:
Técnicamente hablando, tanto los "burladores" como los "espías" son un tipo especial de "dobles de prueba".
Desafortunadamente, Mockito está haciendo la distinción extraña.
Un simulacro en mockito es un simulacro normal en otros marcos de simulacro (le permite stub invocaciones; es decir, devolver valores específicos de llamadas a métodos).
Un espía en mockito es una burla parcial en otros marcos de burla (parte del objeto será burlado y otra parte usará invocaciones de métodos reales).
fuente
Ambos se pueden utilizar para simular métodos o campos. La diferencia es que en el simulacro, estás creando un objeto simulado o falso completo mientras que en el espía, existe el objeto real y solo estás espiando o detectando métodos específicos del mismo.
Mientras que en los objetos espía, por supuesto, dado que es un método real, cuando no está stubing el método, entonces llamará al comportamiento del método real. Si desea cambiar y simular el método, entonces debe eliminarlo.
Considere el ejemplo siguiente como comparación.
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)); } }
¿Cuándo debe usar simulacro o espía? Si desea estar seguro y evitar llamar a servicios externos y solo desea probar la lógica dentro de la unidad, use mock. Si desea llamar a un servicio externo y realizar una llamada de dependencia real, o simplemente decir, desea ejecutar el programa tal como está y solo aplicar métodos específicos, utilice espía. Así que esa es la diferencia entre espiar y simular en mockito.
fuente
TL; versión DR,
Con simulacro , crea una instancia de caparazón desnudo para usted.
Con espía puede burlarse parcialmente de una instancia existente
List<String> spyList = Mockito.spy(new ArrayList<String>());
Caso de uso típico de Spy: la clase tiene un constructor parametrizado, primero desea crear el objeto.
fuente
He creado un ejemplo ejecutable aquí https://www.surasint.com/mockito-with-spy/
Copio algo aquí.
Si tiene algo como este código:
public void transfer( DepositMoneyService depositMoneyService, WithdrawMoneyService withdrawMoneyService, double amount, String fromAccount, String toAccount) { withdrawMoneyService.withdraw(fromAccount,amount); depositMoneyService.deposit(toAccount,amount); }
Es posible que no necesite un espía porque solo puede simular DepositMoneyService y WithdrawMoneyService.
Pero con algún código heredado, la dependencia está en el código de esta manera:
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); }
Sí, puede cambiar al primer código, pero luego se cambia la API. Si este método se usa en muchos lugares, debe cambiarlos todos.
La alternativa es que puede extraer la dependencia de esta manera:
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(); }
Entonces puedes usar el espía para inyectar la dependencia de esta manera:
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();
Más detalles en el enlace de arriba.
fuente
El mejor lugar para comenzar son probablemente los documentos de mockito .
En una nota general, el mockito mock te permite crear stubs.
Crearía un método stub si, por ejemplo, ese método realiza una operación costosa. Digamos, obtiene una conexión a la base de datos, recupera un valor de la base de datos y se lo devuelve a la persona que llama. Obtener la conexión de base de datos puede tardar 30 segundos, lo que ralentiza la ejecución de la prueba hasta el punto en el que probablemente cambie de contexto (o deje de ejecutar la prueba).
Si la lógica que está probando no se preocupa por la conexión de la base de datos, entonces puede reemplazar ese método con un código auxiliar que devuelve un valor codificado.
El espía mockito te permite comprobar si un método llama a otros métodos. Esto puede resultar muy útil cuando se intenta probar el código heredado.
Es útil si está probando un método que funciona a través de los efectos secundarios, entonces usaría un espía mockito. Esto delega las llamadas al objeto real y le permite verificar la invocación del método, la cantidad de veces que se invoca, etc.
fuente
En breve:
@Spy
y@Mock
se utilizan mucho en las pruebas de código, pero los desarrolladores confunden en los casos en que usar uno de ellos y, por lo tanto, los desarrolladores terminan usándolo@Mock
para estar seguros.@Mock
cuando desee probar la funcionalidad externamente sin llamar a ese método.@Spy
cuando desee probar la funcionalidad externamente + internamente con el mismo método al que se llama.A continuación se muestra el ejemplo en el que he tomado el escenario de Election20xx en Estados Unidos.
Los votantes se pueden dividir según
VotersOfBelow21
yVotersOfABove21
.La encuesta de salida ideales Trump dice que va a ganar las elecciones porque
VotersOfBelow21
, yVotersOfABove21
ambos van a votar por decir de triunfo " Hemos elegido Presidente de Trump "Pero este no es el escenario real:
Entonces, ¿cómo se prueba?
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; } }
Ahora tenga en cuenta que en las dos primeras clases anteriores, las personas del grupo de edad dicen que no tienen una mejor opción que Trump. Lo que significa explícitamente que votaron por Trump solo porque no tenían otra opción.
Ahora
ElectionOfYear20XX
dice que Trump ganó porque ambos grupos de edad votaron por él de manera abrumadora.Si probamos el
ElectionOfYear20XX
con @Mock, entonces es posible que no podamos obtener la verdadera razón por la que Trump ganó, solo probaremos la razón externa.Si probamos
ElectionOfYear20XX
con @Spy, obtenemos la verdadera razón por la que Trump ganó con los resultados de la encuesta de salida externa, es decir, internamente + externamente.Nuestra
ELectionOfYear20XX_Test
clase:@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")); } }
Esto debería generar solo los resultados de la prueba lógica, es decir, la verificación externa:
Pruebas tanto
@Spy
externas como internas con la invocación del método real.@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")); } }
Salida:
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
fuente
Me gusta la sencillez de esta recomendación:
Fuente: https://javapointers.com/tutorial/difference-between-spy-and-mock-in-mockito/
Una diferencia común es:
fuente