簡體   English   中英

"如何使用 Mockito 模擬 void 方法"

[英]How to mock void methods with 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();
}

系統不會被模擬觸發。

我想顯示上述系統狀態。 並據此作出斷言。

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

例如,

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

或者如果你想把它與后續行為結合起來,

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

假設您正在考慮在下面的類 World 中模擬設置器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());

我想我已經找到了一個更簡單的答案,只為一個方法調用真正的方法(即使它有一個 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對象上調用 test 中的方法。 例如:

assertEquals(0, spy.methodToTestThatShouldReturnZero());

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

Spy 使我們能夠進行部分模擬。 Mockito 擅長這件事。 因為您的課程不完整,因此您可以通過這種方式模擬該課程中的某些必需位置。

首先:您應該始終導入 mockito static,這樣代碼將更具可讀性(和直觀):

import static org.mockito.Mockito.*;

對於部分模擬並仍然保留其余部分的原始功能,mockito 提供“間諜”。

您可以按如下方式使用它:

private World world = spy(new World());

要消除正在執行的方法,您可以使用以下內容:

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

為方法使用“when”和“thenReturn”提供一些自定義行為:

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

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

如何使用 mockito 模擬 void 方法 - 有兩種選擇:

  1. doAnswer - 如果我們希望我們的模擬 void 方法做某事(盡管無效,但模擬行為)。
  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("old@test.com", "new@test.com");

    //some asserts
    assertThat(customer, is(notNullValue()));
    assertThat(customer.getEmail(), is(equalTo("new@test.com")));

}

@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("old@test.com", "new@test.com");

}
}

您可以在我的文章How to mock with Mockito(帶示例的綜合指南)中找到有關如何使用 Mockito模擬測試void方法的更多詳細信息

在 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());

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

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

在實踐中,它看起來像這樣:

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

或者:

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

我認為您的問題是由於您的測試結構造成的。 我發現很難將模擬與在測試類中實現接口的傳統方法混合使用(正如您在此處所做的那樣)。

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

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

這應該讓您滿意,“世界”正在做正確的事情。

如果您需要在模擬的void方法中進行一些操作,並且您需要操作發送給void方法的參數; 您可以將 Mockito.doAnswer 與 ArgumentCaptor.capture 方法結合使用。

假設您有一個自動裝配GalaxyServiceSpaceService ,它有一個名為someServiceMethod的 void 方法。

您想為SpaceService中調用GalaxyService的 void 方法的方法之一編寫測試。 你的星球也在 SpaceService 中生成。 所以你沒有任何機會嘲笑它。

這是您要為其編寫測試的示例SpaceService類。

class SpaceService {
    @Autowired
    private GalaxyService galaxyService;

    public Date someCoolSpaceServiceMethod() {
        // does something

        Planet planet = new World();
        galaxyService.someServiceMethod(planet); //Planet updated in this method.

        return planet.getCurrentTime();
    }
}

GalaxyService.someServiceMethod方法需要一個 planet 參數。 在方法中做一些事情。 看 :

GalaxyService {
    public void someServiceMethod(Planet planet) {
        //do fancy stuff here. about solar system etc.

        planet.setTime(someCalculatedTime); // the thing that we want to test.

        // some more stuff.
    }
}

並且您想測試此功能。

這是一個例子:

ArgumentCaptor<World> worldCaptor = ArgumentCaptor.forClass(World.class);
Date testDate = new Date();

Mockito.doAnswer(mocked-> {
    World capturedWorld = worldCaptor.getValue();
    world.updateTime(testDate);
    return null;
}).when(galaxyService.someServiceMethod(worldCaptor.capture());

Date result = spaceService.someCoolSpaceServiceMethod();

assertEquals(result, testDate);

根據不同的場景,可以通過2種方式來完成:

  1. doAnswer-如果我們希望模擬的void方法做某事(盡管無效,也要模擬行為)。

  2. doThrow-如果您想從模擬的void方法拋出異常,我們也有Mockito.doThrow()。

在您的示例中,您應該模擬 Listener 項目並使用 Mockito.verify 檢查與它的交互

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM