Как издеваться над методами с типом возврата 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();
}
Система не запускается с макетом. знак равно Я хочу показать вышеупомянутое состояние системы. И сделайте утверждение в соответствии с ними.
Решение так называемой проблемы заключается в использовании spy
Mockito.spy(...) вместо mock
Mockito.mock(..).
Шпион позволяет нам частично насмехаться. Мокито хорош в этом вопросе. Поскольку у вас есть класс, который не является полным, таким образом вы издеваетесь над каким-то необходимым местом в этом классе.
Взгляните на документы 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());
Я думаю, что нашел более простой ответ на этот вопрос, чтобы вызвать реальный метод только для одного метода (даже если он имеет возврат void), вы можете сделать это:
Mockito.doCallRealMethod().when(<objectInstance>).<method>();
<objectInstance>.<method>();
Или вы могли бы назвать реальный метод для всех методов этого класса, выполнив следующее:
<Object> <objectInstance> = mock(<Object>.class, Mockito.CALLS_REAL_METHODS);
Добавляя к тому, что сказал @sateesh, когда вы просто хотите высмеять метод void, чтобы предотвратить вызов этого вызова, вы можете использовать Spy
следующим образом:
World world = new World();
World spy = Mockito.spy(world);
Mockito.doNothing().when(spy).methodToMock();
Если вы хотите запустить свой тест, убедитесь, что вы вызываете метод в тесте на объекте Spy
, а не на объекте world
. Например:
assertEquals(0,spy.methodToTestThatShouldReturnZero());
Прежде всего: вы всегда должны импортировать статику 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 в документе.
Как высмеивать недействительные методы с помощью 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 (всестороннее руководство с примерами)
Добавление другого ответа на кучу (не предназначен для каламбура)...
Вам нужно вызвать метод doAnswer, если вы не можете\не использовать шпион. Однако вам необязательно катить свой Answer. Существует несколько реализаций по умолчанию. Примечательно, CallsRealMethods.
На практике это выглядит примерно так:
doAnswer(new CallsRealMethods()).when(mock)
.voidMethod(any(SomeParamClass.class));
Или:
doAnswer(Answers.CALLS_REAL_METHODS.get()).when(mock)
.voidMethod(any(SomeParamClass.class));
Я думаю, ваши проблемы связаны с вашей тестовой структурой. Мне было трудно смеяться над традиционным методом реализации интерфейсов в тестовом классе (как вы это сделали).
Если вы реализуете слушателя как макет, вы можете проверить взаимодействие.
Listener listener = mock(Listener.class);
w.addListener(listener);
world.doAction(..);
verify(listener).doAction();
Это должно удовлетворить вас тем, что "мир" поступает правильно.
@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); } }