it-roy-ru.com

В чем разница между шуткой и шпионажем при использовании Mockito?

Какой был бы вариант использования шпиона Mockito?

Мне кажется, что каждый случай использования шпиона может быть обработан с помощью макета, используя callRealMethod.

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

121
Victor Grazi

Ответ в документация :

Реальные частичные издевательства (С 1.8.0)

Наконец, после многих внутренних дискуссий и обсуждений в списке рассылки, в Mockito была добавлена ​​частичная поддержка. Ранее мы рассматривали частичные насмешки как запахи кода. Тем не менее, мы нашли законный вариант использования для частичных макетов - подробнее: здесь

До релиза 1.8 spy () не производил реальных частичных насмешек, и это приводило некоторых в замешательство.

callRealMethod() была введена после spy(), но spy () была оставлена ​​там, конечно, для обеспечения обратной совместимости.

В противном случае, вы правы: все методы шпиона реальны, если не зарезаны. Все методы макета являются заглушками, если не вызывается callRealMethod(). В общем, я бы предпочел использовать callRealMethod(), потому что это не заставляет меня использовать идиому doXxx().when() вместо традиционной when().thenXxx()

87
JB Nizet

Разница между шпионом и макетом

Когда Mockito создает макет - он делает это из класса типа, а не из фактического экземпляра. Макет просто создает пустой экземпляр Shell класса, полностью оснащенный для отслеживания взаимодействий с ним. С другой стороны, шпион обернет существующий экземпляр. Он по-прежнему будет вести себя так же, как и обычный экземпляр, с той лишь разницей, что он также будет оснащен инструментами для отслеживания всех взаимодействий с ним.

В следующем примере - мы создаем макет класса ArrayList:

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

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

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

Как вы можете видеть - добавление элемента в макетированный список на самом деле ничего не добавляет - он просто вызывает метод без других побочных эффектов. С другой стороны, шпион будет вести себя по-другому - он на самом деле вызовет реальную реализацию метода add и добавит элемент в базовый список:

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

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

Здесь мы можем с уверенностью сказать, что был вызван настоящий внутренний метод объекта, потому что когда вы вызываете метод size (), вы получаете размер как 1, но этот метод size () не подвергался насмешке! Так откуда же взялся 1? Внутренний реальный метод size () вызывается как size (), а не насмешливым (или заглушенным) скажем, что запись была добавлена ​​к реальному объекту.

Источник: http://www.baeldung.com/mockito-spy + собственные заметки.

71
Saurabh Patil

Если есть объект с 8 методами, и у вас есть тест, в котором вы хотите вызвать 7 реальных методов и один метод заглушки, у вас есть два варианта:

  1. Используя макет, вы должны настроить его, вызвав 7 callRealMethod и заглушив один метод.
  2. Используя spy, вы должны настроить его, заглушив один метод

официальная документация на doCallRealMethod рекомендует использовать шпиона для частичной проверки.

См. Также javadoc spy (Object), чтобы узнать больше о частичных имитациях. Mockito.spy () - рекомендуемый способ создания частичных имитаций. Причина в том, что это гарантирует, что реальные методы вызываются против правильно сконструированного объекта, потому что вы несете ответственность за создание объекта, переданного методу spy ().

32
user2412398

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

Я создал выполнимый пример здесь 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();

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

4
Surasin Tancharoen