Как издеваться над пустыми методами с помощью Mockito

Как смоделировать методы с возвращаемым типом void?

Я применил шаблон наблюдателя, но не могу высмеять его с помощью 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();
}

Система не запускается с помощью макета.

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

Ответ 1

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

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

Ответ 2

Взгляните на документы 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());

Ответ 3

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

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

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

<Object> <objectInstance> = mock(<Object>.class, Mockito.CALLS_REAL_METHODS);

Ответ 4

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

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

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

assertEquals(0,spy.methodToTestThatShouldReturnZero());

Ответ 5

Прежде всего: вы всегда должны импортировать статику 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 в документе.

Ответ 6

Как высмеивать недействительные методы с помощью 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 (всестороннее руководство с примерами)

Ответ 7

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

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

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

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

Или:

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

Ответ 8

В Java 8 это можно сделать немного чище, если у вас есть статический импорт для org.mockito.Mockito.doAnswer:

doAnswer(i -> {
  // Do stuff with i.getArguments() here
  return null;
}).when(*mock*).*method*(*methodArguments*);

return null; это важно, и без него компиляция завершится с некоторыми довольно неясными ошибками, поскольку она не сможет найти подходящее переопределение для doAnswer.

Например, ExecutorService который просто немедленно выполняет любой Runnable переданный execute() может быть реализован с использованием:

doAnswer(i -> {
  ((Runnable) i.getArguments()[0]).run();
  return null;
}).when(executor).execute(any());

Ответ 9

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

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

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

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

Ответ 10

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