Как сделать mock to void методами с mockito

646

Как издеваться над методами с типом возврата void?

Я реализовал шаблон Observer, но я не могу издеваться над ним с Mockito, потому что я не знаю, как это сделать.

И я попытался найти пример в Интернете, но не смог.

Мой класс выглядит как

public class World {

    List<Listener> listeners;

    void addListener(Listener item) {
        listeners.add(item);
    }

    void doAction(Action goal,Object obj) {
        setState("i received");
        goal.doAction(obj);
        setState("i finished");
    }

    private string state;
    //setter getter state
} 

public class WorldTest implements Listener {

    @Test public void word{
    World  w= mock(World.class);
    w.addListener(this);
    ...
    ...

    }
}

interface Listener {
    void doAction();
}

Система не запускается с макетом. знак равно Я хочу показать вышеупомянутое состояние системы. И сделайте утверждение в соответствии с ними.

  • 3
    Остерегайтесь того, что методы void для mocks ничего не делают по умолчанию!
Теги:
unit-testing
mockito
mocking

9 ответов

39
Лучший ответ

Решение так называемой проблемы заключается в использовании spy Mockito.spy(...) вместо mock Mockito.mock(..).

Шпион позволяет нам частично насмехаться. Мокито хорош в этом вопросе. Поскольку у вас есть класс, который не является полным, таким образом вы издеваетесь над каким-то необходимым местом в этом классе.

  • 2
    Я наткнулся здесь, потому что у меня была похожая проблема (также, по совпадению, случилось, что я тестировал взаимодействие субъекта / наблюдателя). Я уже использую шпиона, но я хочу, чтобы метод SubjectChanged делал что-то другое. Я мог бы использовать `verify (наблюдатель) .subjectChanged (subject) просто чтобы увидеть, что метод был вызван. Но по какой-то причине я бы предпочел переопределить метод. Для этого, комбинация подхода Сатиша и вашего ответа здесь была способом пойти ...
  • 29
    Нет, на самом деле это не поможет с насмешливыми методами. Хитрость заключается в том, чтобы использовать один из четырех статических методов Mockito, перечисленных в ответе Сатиша.
Показать ещё 2 комментария
871

Взгляните на документы API Mockito API. Как упоминается связанный документ (пункт № 12), вы можете использовать любое из семейств методов doThrow(), doAnswer(), doNothing(), doReturn() из фреймворка Mockito, чтобы издеваться над методами void.

Например,

Mockito.doThrow(new Exception()).when(instance).methodName();

или если вы хотите совместить его с последующим поведением,

Mockito.doThrow(new Exception()).doNothing().when(instance).methodName();

Предполагая, что вы смотрите на насмешку setter setState(String s) в классе World ниже, в коде используется метод doAnswer, чтобы издеваться над setState.

World  mockWorld = mock(World.class); 
doAnswer(new Answer<Void>() {
    public Void answer(InvocationOnMock invocation) {
      Object[] args = invocation.getArguments();
      System.out.println("called with arguments: " + Arrays.toString(args));
      return null;
    }
}).when(mockWorld).setState(anyString());
  • 8
    @qualidafial: Да, я думаю, что параметризация для Void была бы лучше, так как она лучше говорит о том, что меня не интересует тип возвращаемого значения. Я не знал об этой конструкции, спасибо за указание на это.
  • 2
    doThrow сейчас # 5 (также для меня, используя doThrow, это исправило сообщение "тип 'void' здесь запрещен", для подписчиков ...)
Показать ещё 4 комментария
71

Я думаю, что нашел более простой ответ на этот вопрос, чтобы вызвать реальный метод только для одного метода (даже если он имеет возврат void), вы можете сделать это:

Mockito.doCallRealMethod().when(<objectInstance>).<method>();
<objectInstance>.<method>();

Или вы могли бы назвать реальный метод для всех методов этого класса, выполнив следующее:

<Object> <objectInstance> = mock(<Object>.class, Mockito.CALLS_REAL_METHODS);
  • 10
    Это настоящий ответ прямо здесь. Метод spy () работает нормально, но обычно зарезервирован для случаев, когда вы хотите, чтобы объект выполнял почти все нормально.
  • 1
    Что это значит? Вы на самом деле вызываете методы? Я действительно не использовал mockito раньше.
Показать ещё 1 комментарий
39

Добавляя к тому, что сказал @sateesh, когда вы просто хотите высмеять метод void, чтобы предотвратить вызов этого вызова, вы можете использовать Spy следующим образом:

World world = new World();
World spy = Mockito.spy(world);
Mockito.doNothing().when(spy).methodToMock();

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

assertEquals(0,spy.methodToTestThatShouldReturnZero());
17

Прежде всего: вы всегда должны импортировать статику mockito, таким образом, код будет намного читабельнее (и интуитивно понятным):

import static org.mockito.Mockito.*;

Для частичного издевательств и сохранения исходной функциональности остальное mockito предлагает "Spy".

Вы можете использовать его следующим образом:

private World world = spy(World.class);

Чтобы исключить выполнение метода, вы можете использовать что-то вроде этого:

doNothing().when(someObject).someMethod(anyObject());

чтобы дать некоторое пользовательское поведение методу, используйте "когда" с "thenReturn":

doReturn("something").when(this.world).someMethod(anyObject());

Для получения дополнительных примеров, пожалуйста, найдите образцы mockito в документе.

  • 2
    Как статический импорт связан с тем, чтобы сделать его более читабельным?
  • 0
    буквально ничего.
Показать ещё 1 комментарий
11

Как высмеивать недействительные методы с помощью mockito - есть два варианта:

  • doAnswer - Если мы хотим, чтобы наш издеваемый метод void что-то делал (высмеивал поведение, несмотря на то, что он был недействительным).
  • doThrow - Тогда существует Mockito.doThrow(), если вы хотите выбросить исключение из метода mocked void.

Ниже приведен пример того, как его использовать (не идеальный usecase, а просто хотел проиллюстрировать основное использование).

@Test
public void testUpdate() {

    doAnswer(new Answer<Void>() {

        @Override
        public Void answer(InvocationOnMock invocation) throws Throwable {
            Object[] arguments = invocation.getArguments();
            if (arguments != null && arguments.length > 1 && arguments[0] != null && arguments[1] != null) {

                Customer customer = (Customer) arguments[0];
                String email = (String) arguments[1];
                customer.setEmail(email);

            }
            return null;
        }
    }).when(daoMock).updateEmail(any(Customer.class), any(String.class));

    // calling the method under test
    Customer customer = service.changeEmail("[email protected]", "[email protected]");

    //some asserts
    assertThat(customer, is(notNullValue()));
    assertThat(customer.getEmail(), is(equalTo("[email protected]")));

}

@Test(expected = RuntimeException.class)
public void testUpdate_throwsException() {

    doThrow(RuntimeException.class).when(daoMock).updateEmail(any(Customer.class), any(String.class));

    // calling the method under test
    Customer customer = service.changeEmail("[email protected]", "[email protected]");

}
}

Вы можете найти более подробную информацию о методах mock и test void с помощью Mockito в моем сообщении Как насмехаться с Mockito (всестороннее руководство с примерами)

  • 1
    Отличный пример. Примечание. В Java 8 было бы немного лучше использовать лямбду вместо анонимного класса: вызов doAnswer ((Answer <Void>) -> {// CODE}). When (mockInstance) .add (метод ());»
10

Добавление другого ответа на кучу (не предназначен для каламбура)...

Вам нужно вызвать метод doAnswer, если вы не можете\не использовать шпион. Однако вам необязательно катить свой Answer. Существует несколько реализаций по умолчанию. Примечательно, CallsRealMethods.

На практике это выглядит примерно так:

doAnswer(new CallsRealMethods()).when(mock)
        .voidMethod(any(SomeParamClass.class));

Или:

doAnswer(Answers.CALLS_REAL_METHODS.get()).when(mock)
        .voidMethod(any(SomeParamClass.class));
4

Я думаю, ваши проблемы связаны с вашей тестовой структурой. Мне было трудно смеяться над традиционным методом реализации интерфейсов в тестовом классе (как вы это сделали).

Если вы реализуете слушателя как макет, вы можете проверить взаимодействие.

Listener listener = mock(Listener.class);
w.addListener(listener);
world.doAction(..);
verify(listener).doAction();

Это должно удовлетворить вас тем, что "мир" поступает правильно.

0

@ashley: работает для меня

public class AssetChangeListenerImpl extends
AbstractAssetChangeListener implements AssetChangeListener {

  @Override
  public void onChangeEvent(final EventMessage message) throws EventHubClientException {
      execute(message);
    }
  }
}

public class AbstractAssetChangeListener {
  protected  void execute( final EventMessage message ) throws EventHubClientException {
    executor.execute( new PublishTask(getClient(), message) );
} } @RunWith(MockitoJUnitRunner.class) public class AssetChangeListenerTest extends AbstractAssetChangeListenerTest {

 public void testExecute() throws EventHubClientException {
    EventMessage message = createEventMesage(EventType.CREATE);
    assetChangeListener.execute(message);
    verify(assetChangeListener, times(1)).execute(message);
} }

Ещё вопросы

Сообщество Overcoder
Наверх
Меню