2010-02-16 2 views
679

Как издеваться над методами с типом возврата void?Как сделать макет для недействительных методов с помощью mockito

Я реализовал шаблон 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(); 
} 

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

+0

Помните, что недействительные методы на издеваешься ничего не делать по умолчанию! – Line

ответ

43

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

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

+2

Я наткнулся на это, потому что у меня была аналогичная проблема (также, по-видимому, случилось тестирование взаимодействия Subject/Observer). Я уже использую шпиона, но мне нужен метод SubjectChanged сделать что-то другое. Я мог бы использовать 'verify (observer) .subjectChanged (subject) jus t, чтобы увидеть, что метод был вызван. Но по какой-то причине я бы скорее отказался от метода. Для этого комбинация подхода Sateesh и вашего ответа здесь была способ пойти ... – gMale

+26

Нет, это не поможет с насмешливыми методами пустоты. Хитрость заключается в использовании одного из четырех статических методов Mockito, перечисленных в ответе sateesh. –

+0

Что делать, если класс абстрактный, вы не можете шпионить затем правильно? – Gurnard

876

Посмотрите на Mockito API docs. Поскольку связанный документ упоминает (Point # 12), вы можете использовать любого из doThrow(), doAnswer() , doNothing(), doReturn() семейства методов из рамок Mockito издеваться недействительными методы.

Например,

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

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

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

Предположив, что вы смотрите на насмешливый сеттер 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()); 
+7

@qualidafial: Да, я думаю, что параметризация для Void будет лучше, поскольку она лучше передает, что меня не интересует тип возврата. Я не знал об этой конструкции, спасибо, что указал на это. – sateesh

+1

doThrow is # 5 now (также для меня с помощью doThrow это исправлено сообщение «'void' type не разрешено здесь», для последователей ...) – rogerdpack

+0

@qualidafial: Я думаю, что возвращаемый тип вызова Answer.answer - это не то, что возвращается к исходному методу, это то, что возвращается на вызов doAnswer, предположительно, если вы хотите сделать что-то еще с этим значением в своем тесте. – twelve17

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

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

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

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

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

Это настоящий ответ прямо здесь. Метод spy() работает нормально, но обычно он зарезервирован для того, когда вы хотите, чтобы объект выполнял большинство всего, как обычно. – biggusjimmus

+1

Что это значит? Вы на самом деле называете методы? Раньше я раньше не использовал mockito. – obesechicken13

+0

Да, макет будет называть реальными методами. Если вы используете @Mock, вы можете указать то же самое: @Mock (answer = Answers.CALLS_REAL_METHODS) , чтобы получить те же результаты. – Ale

4

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

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

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

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

41

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

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

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

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

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

import static org.mockito.Mockito.*; 

Для частичных насмешек и сохраняя первоначальную функциональность на остальном mockito предлагает «Шпион».

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

private World world = spy(World.class); 

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

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

дать некоторые пользовательского поведения к использованию метода «, когда "с" thenReturn ":

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

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

+0

Что делает статический импорт, чтобы сделать его более читаемым? – functioncall

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

Как издеваются недействительные методы с Mockito - есть два варианта:

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

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

} 
} 

Вы можете найти более подробную информацию о том, как макет и испытанийнедействительных методов с Mockito в моем посте How to mock with Mockito (A comprehensive guide with examples)

+1

Отличный пример. Примечание: В Java 8, это может быть немного лучше использовать лямбда вместо вместо annonymous класса:. «doAnswer ((Ответ ) вызов -> { // КОД }), когда (mockInstance) .add (метод());» – miwe

3

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

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

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

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

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