java mockito中文 - 如何使用mockito模擬void方法




mockito教學 junit (9)

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

如何用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();
}

系統不會被模擬觸發。 =(我想顯示上面提到的系統狀態,並根據它們進行斷言。


為這一堆添加另一個答案(沒有雙關語意思)...

如果你不能\不想使用間諜,你需要調用doAnswer方法。 但是,您不一定需要推出自己的Answer 。 有幾個默認實現。 值得注意的是, CallsRealMethods

實際上,它看起來像這樣:

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

要么:

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

首先:你應該總是導入mockito static,這樣代碼將更加可讀(和直觀):

import static org.mockito.Mockito.*;

對於部分嘲諷,仍然保持原有的功能,其餘mockito提供“間諜”。

你可以使用它如下:

private World world = spy(World.class);

為了消除一個方法被執行,你可以使用這樣的東西:

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

給一個方法使用“when”時使用“thenReturn”一些自定義行為:

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

有關更多示例,請在文檔中找到mockito示例。


我認為你的問題是由於你的測試結構。 我發現很難將嘲諷與在測試類中實現接口的傳統方法混淆(正如您在此處所做的那樣)。

如果您將偵聽器實現為模擬,則可以驗證交互。

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

這應該讓你滿意,'世界'正在做正確的事情。


看看Mockito API文檔 。 正如鍊接文件提到的那樣(第12點),您可以使用doThrow()任何doThrow()doAnswer()doNothing()doReturn()系列方法來模擬void方法。

例如,

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

或者如果您想將其與後續行為相結合,

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

假設你正在尋找模擬setter setState(String s)類的Setter setState(String s) ,下面的代碼使用了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());

在Java 8中,假設你有一個用於org.mockito.Mockito.doAnswer的靜態導入,它可以做得更乾淨org.mockito.Mockito.doAnswer

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

return null; 是非常重要的,如果沒有它,編譯將會失敗並出現一些相當模糊的錯誤,因為它無法為doAnswer找到合適的覆蓋。

例如,一個ExecutorService只需立即執行任何傳遞給execute() Runnable就可以實現:

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

所謂問題的解決方案是使用spy Mockito.spy(...)而不是mock Mockito.mock(..)

間諜使我們能夠部分嘲笑。 Mockito擅長這件事。 因為你有不完整的課程,所以你在這個課堂上嘲笑了一些必要的地方。


如何用mockito模擬無效方法 - 有兩種選擇:

  1. doAnswer - 如果我們想要我們的虛擬無效方法來做一些事情(儘管是無效的嘲笑行為)。
  2. doThrow - 如果你想從Mockito.doThrow()的void方法拋出一個異常,那麼就有Mockito.doThrow()

以下是如何使用它的例子(不是一個理想的用例,但只是想說明基本用法)。

@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 模擬測試 void方法的細節。如何用Mockito 模擬(一個帶有例子的綜合指南)


使用Mockito的間諜方法進行部分嘲弄可能是解決您的問題的方法,正如上面的答案中所述。 在某種程度上,我同意,對於具體的用例,嘲笑數據庫查找可能更合適。 根據我的經驗,這並不總是可行的 - 至少不是沒有其他解決方法 - 我認為這是非常麻煩或至少是脆弱的。 請注意,部分嘲諷不適用於盟友版本的Mockito。 您至少使用1.8.0。

我只是寫了一個簡單的原始問題的評論,而不是發布這個答案,但不允許這樣做。

還有一件事:我真的無法理解,在這裡問一個問題的次數很多時會在“你為什麼要這麼做”的問題上得到評論,而沒有至少試圖理解問題。 特別是當涉及到需要部分模擬的時候,真的有很多用例可以想像它會在哪裡有用。 這就是Mockito提供這種功能的原因。 這個功能當然不會被過度使用。 但是當我們談論測試用例設置,否則無法以非常複雜的方式建立時,應該使用間諜。





java unit-testing mocking mockito