2015-02-03 5 views
37

Mockito - Я понимаю, что шпион вызывает реальные методы на объекте, а макет вызывает методы на двойном объекте. Также следует избегать шпионов, если нет запаха кода. Однако, как работают шпионы, и когда я должен их использовать? Как они отличаются от насмешек?Mockito - spy vs mock

+1

Возможный дубликат [mockito mock vs. spy] (http://stackoverflow.com/questions/15052984/mockito-mock-vs-spy) – rds

+0

Возможный дубликат [Mocking vs. Spying in mocking frameworks] (http: //stackoverflow.com/questions/12827580/mocking-vs-spying-in-mocking-frameworks) – PenguinEngineer

ответ

6

Лучшее место для начала, вероятно, the docs for mockito.

Общеизвестно, что mockito mock позволяет создавать заглушки.

Вы должны создать метод заглушки, если, например, этот метод выполняет дорогостоящую операцию. Скажем, он получает соединение с базой данных, извлекает значение из базы данных и возвращает его вызывающему. Получение соединения db может занять 30 секунд, что замедлит выполнение теста до того момента, когда вы, вероятно, переключитесь на контекст (или прекратите выполнение теста).

Если логика, которую вы тестируете, не заботится о соединении с базой данных, вы можете заменить этот метод заглушкой, которая возвращает твердое кодированное значение.

Шпиона mockito позволяет проверить, вызывает ли метод другие методы. Это может быть очень полезно при попытке тестирования устаревшего кода.

Полезно, если вы тестируете метод, который работает через побочные эффекты, тогда вы будете использовать шпион-шпионаж. Этот делегат обращается к реальному объекту и позволяет вам проверять вызов метода, количество раз, вызванное и т. Д.

35

Технически говоря, «макеты» и «шпионы» - это особый тип «тестовых удвоений».

Mockito, к сожалению, делает различия странными.

Макет в mockito - это нормальный макет в других насмешливых фреймворках (позволяет вам заглушать вызовы, то есть возвращать определенные значения из вызовов методов).

Шпион в mockito является частичным макетом в других насмешливых фреймворках (часть объекта будет издеваться, а часть будет использовать реальные вызовы метода).

+1

Итак, когда использовать один над другим? –

+0

@IgorGanapolsky: это зависит от вашего прецедента. :-) – Sipty

8

Я создал ruanble пример здесь 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 изменяется. Если этот метод используется во многих местах, вы должны изменить все из них.

Alternative является то, что вы можете извлечь зависимость такой:

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(); 

Более подробно в ссылке выше.

2

TL; DR версия,

С макетом, он создает экземпляр оболочки голой кости для вас.

List<String> mockList = Mockito.mock(ArrayList.class); 

С шпиона вы можете частично издеваться на экземпляре существующие

List<String> spyList = Mockito.spy(new ArrayList<String>()); 

типичный случай для Spy: класс имеет параметризированный конструктор, вы хотите создать объект первым.

0

Оба могут использоваться для издевательства методов или полей. Разница в том, что в макете вы создаете полный макет или фальшивый объект во время шпиона, есть реальный объект, и вы просто шпионируете или выполняете определенные методы.

Хотя в объектах шпиона, конечно, поскольку это реальный метод, когда вы не стучите по методу, он будет вызывать поведение реального метода. Если вы хотите изменить и высмеять метод, вам нужно его заглушить.

Рассмотрим приведенный ниже пример для сравнения.

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

Когда вы используете макет или шпион? Если вы хотите быть в безопасности и избегать вызова внешних служб и просто хотите протестировать логику внутри устройства, используйте макет. Если вы хотите вызвать внешнюю службу и выполнить вызов реальной зависимости, или просто сказать, что вы хотите запустить программу, как она есть, и просто заглушить конкретные методы, а затем использовать шпион. Вот в чем разница между шпионами и макетом в mockito.

Смежные вопросы