Mockito - я понимаю, что шпион вызывает реальные методы объекта, а макет вызывает методы двойного объекта. Также следует избегать шпионов, если нет запаха кода. Однако как работают шпионы и когда их на самом деле следует использовать? Чем они отличаются от моков?
99
Ответы:
Технически говоря, и «издевательства», и «шпионы» - это особый вид «тестовых двойников».
К сожалению, Mockito делает различие странным.
Мок в mockito - это обычный имитация в других фреймворках фиксации (позволяет заглушать вызовы, то есть возвращать определенные значения из вызовов методов).
Шпион в mockito - это частичный имитатор в других фреймворках имитирования (часть объекта будет имитироваться, а часть будет использовать вызовы реальных методов).
источник
Оба могут использоваться для имитации методов или полей. Разница в том, что в макете вы создаете полный фиктивный или фальшивый объект, в то время как в шпионаже есть настоящий объект, и вы просто шпионите или заглушаете определенные его методы.
Конечно, в объектах-шпионах, поскольку это настоящий метод, когда вы не закрываете метод, он вызывает реальное поведение метода. Если вы хотите изменить и поиздеваться над методом, вам нужно его заглушить.
Рассмотрим приведенный ниже пример для сравнения.
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)); } }
Когда следует использовать mock или spy? Если вы хотите быть в безопасности и избегать вызова внешних служб и просто хотите проверить логику внутри модуля, используйте mock. Если вы хотите вызвать внешнюю службу и выполнить вызов реальной зависимости или просто сказать, что вы хотите запустить программу, как она есть, и просто заглушить определенные методы, используйте шпион. В этом разница между шпионажем и издевательством в mockito.
источник
TL; версия DR,
С помощью mock он создает для вас экземпляр оболочки с голой костью.
С помощью шпиона вы можете частично имитировать существующий экземпляр
List<String> spyList = Mockito.spy(new ArrayList<String>());
Типичный вариант использования Spy: у класса есть параметризованный конструктор, вы хотите сначала создать объект.
источник
Я создал исполняемый пример здесь https://www.surasint.com/mockito-with-spy/
Я скопирую кое-что здесь.
Если у вас есть что-то вроде этого кода:
public void transfer( DepositMoneyService depositMoneyService, WithdrawMoneyService withdrawMoneyService, double amount, String fromAccount, String toAccount) { withdrawMoneyService.withdraw(fromAccount,amount); depositMoneyService.deposit(toAccount,amount); }
Возможно, вам не понадобится шпион, потому что вы можете просто имитировать DepositMoneyService и WithdrawMoneyService.
Но с некоторым устаревшим кодом зависимость находится в таком коде:
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); }
Да, вы можете перейти на первый код, но затем API изменится. Если этот метод используется во многих местах, вы должны изменить их все.
Альтернативой является то, что вы можете извлечь зависимость следующим образом:
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(); }
Затем вы можете использовать шпион, чтобы ввести зависимость следующим образом:
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();
Подробнее по ссылке выше.
источник
Лучше всего начать с документации по mockito .
В общем, mockito mock позволяет создавать заглушки.
Вы могли бы создать метод-заглушку, если, например, этот метод выполняет дорогостоящую операцию. Скажем, он получает соединение с базой данных, извлекает значение из базы данных и возвращает его вызывающей стороне. Установление соединения с базой данных может занять 30 секунд, замедляя выполнение теста до точки, при которой вы, вероятно, переключитесь на контекст (или остановите выполнение теста).
Если логика, которую вы тестируете, не заботится о соединении с базой данных, вы можете заменить этот метод заглушкой, которая возвращает жестко закодированное значение.
Шпион mockito позволяет вам проверить, вызывает ли метод другие методы. Это может быть очень полезно при попытке протестировать устаревший код.
Полезно, если вы тестируете метод, который работает с побочными эффектами, тогда вы должны использовать шпион mockito. Это делегирует вызовы реальному объекту и позволяет вам проверять вызов метода, количество вызовов и т. Д.
источник
Короче говоря:
@Spy
и@Mock
широко используются при тестировании кода, но разработчики действительно путают случаи, когда использовать один из них, и, таким образом, разработчики в конечном итоге используют их в@Mock
целях безопасности.@Mock
если вы хотите просто протестировать функциональность извне, не вызывая этот метод.@Spy
когда вы хотите протестировать функциональность извне + внутри с помощью самого вызываемого метода.Ниже приведен пример сценария Election20xx в Америке.
Избирателей можно разделить на
VotersOfBelow21
иVotersOfABove21
.Идеальный выход опрос говорит , что Trump выиграет выборы , потому что
VotersOfBelow21
иVotersOfABove21
оба будут голосовать за козырем говоря : « Мы выбрали президента Трампа »Но это не настоящий сценарий:
Так как же это проверить ??
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; } }
Теперь обратите внимание, что в первых двух классах выше, люди обеих возрастных групп говорят, что у них нет лучшего выбора, чем козырь. Это явно означает, что они проголосовали за Трампа только потому, что у них не было выбора.
Теперь
ElectionOfYear20XX
говорится, что Трамп победил, потому что обе возрастные группы проголосовали за него подавляющим большинством.Если бы мы протестировали с
ElectionOfYear20XX
помощью @Mock, то, возможно, не смогли бы понять истинную причину победы Трампа, мы просто проверим внешнюю причину.Если мы проверим
ElectionOfYear20XX
с помощью @Spy, то по результатам внешнего экзит-опроса мы поймем настоящую причину, по которой Трамп победил, то есть внутренне + внешне.Наши
ELectionOfYear20XX_Test
класс:@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")); } }
Это должно выводить только результаты логического теста, т.е. внешнюю проверку:
Тестирование с
@Spy
внешней, так и с внутренней стороны с фактическим вызовом метода.@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")); } }
Выход:
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
источник
Мне нравится простота этой рекомендации:
Источник: https://javapointers.com/tutorial/difference-between-spy-and-mock-in-mockito/
Общее различие:
источник