簡體   English   中英

Mockito 驗證模擬 object 上的最后一次調用

[英]Mockito verify the last call on a mocked object

我有一些需要測試的邏輯,例如:

{
    ...
    A.add("1");
    ...
    A.add("what ever");
    ...
    A.add("2");
    A.delete("5");
    ...
}

我已經在我的測試中模擬了 A 並且我可以測試 add 方法在參數(“2”)上被調用一次,例如:

Mockito.verify(mockedA).add("2");

我的問題是如何測試我是否可以驗證方法 add 的最后一次調用是 add("2") 而不是其他 arguments。

因為如果有人不小心在最后添加了另一個調用,例如 add("3") ,上面的測試無法捕獲。 請注意,之后我們不關心 A 上的其他方法調用。 我們也不關心調用方法的次數,調用方法的順序。 這里的關鍵點是我們是否可以驗證某個 mockedObject 的某個方法的最后一個 true 參數。

如果你問你為什么需要這樣的功能,我會說在現實世界中我們可能需要處理一些邏輯來設置一些東西並且最后一組獲勝,並且為了避免某人意外地設置一些其他的東西我會喜歡用我們的 UT 來捕捉這個。 為了不讓測試過於復雜和整潔,所以我只希望驗證 object 的某個方法的最后一次調用,而不是驗證諸如 order/noMoreInteractions/AtMostTimes 之類的東西。

關於調用的順序

默認情況下, Mockito.verify()與調用順序無關。
要將其考慮在內,請將模擬包裝在InOrder實例中,並在此實例上執行調用驗證。

關於沒有更多的互動

如果在您要驗證的方法之后不再調用mock,則可以使用Mockito.verifyNoMoreInteractions(Object... mocks)檢查任何給定的模擬是否有任何未經驗證的交互,例如:

InOrder inOrder = Mockito.inOrder(mockedA);
inOrder.verify(mockedA).add("1");
inOrder.verify(mockedA).add("2");
Mockito.verifyNoMoreInteractions(mockedA);

如果仍然可以在要驗證的方法之后調用verify(T mock, VerificationMode mode)可以在驗證調用以verify(T mock, VerificationMode mode)通過傳遞VerificationMode來添加,該VerificationMode檢查最多執行了2次調用。

InOrder inOrder = Mockito.inOrder(mockedA);
inOrder.verify(mockedA).add("1");
inOrder.verify(mockedA).add("2");
Mockito.verify(mockedA, Mockito.atMost(2)).add(Mockito.anyString());

關於你的思考和這種嘲弄方式的警告

因為上面的測試無法捕獲,如果有人偶然添加了另一個調用,例如add(“3”)。

Mockito提供了一個強大而廣泛的工具包來處理模擬。 某些功能(例如驗證)更具體地證實,沒有檢測到關於模擬或模擬的特定方法的更多交互, 使得您的測試更難以閱讀和維護
同樣,目前您要檢查模擬上的調用是否按特定順序執行。 但是,您通常只希望根據業務/邏輯方案而非技術調用的要求使用這些檢查。
例如,假設在測試方法中你有一個案例,出於商業原因,模擬方法被調用3次,另一種情況是模擬方法被調用2次。 在兩次預期調用的情況下檢查它是否僅被調用2次而不是更多是有意義的。
但是一般來說,你應該小心謹慎,你的單元測試不會過度使用模擬驗證,這可能看起來像是對流的描述的斷言,而不是對行為/邏輯的斷言。

感謝@ staszko032,受ArgumentCaptor的啟發,而不是getAllValues並驗證序列,我們可以使用captor的getValue,因為captor的getValue總是得到最后一個真正的參數。 我們可以這樣做:

    ArgumentCaptor<String> captor = ArgumentCaptor.forClass(String.class);
    Mockito.verify(mockedA, Mockito.atLeastOnce()).add(captor.capture());
    Assert.assertEquals("2", captor.getValue());

這對我來說是你在嘲笑數據類。 根據我的經驗,最好留下(有狀態的)數據類和模擬(無狀態)服務。 這樣,您可以驗證測試中的方法是否生成正確的數據,而不僅僅是驗證一系列調用。 與testdata構建器一起(使用某種默認狀態實例化數據類很容易,例如使用構建器模式),編寫測試變得非常容易。

如果確實需要模擬,那么測試所需內容的唯一方法是使用InOrder ,並驗證模擬上的每個調用,並以verifyNoMoreInteractions結束。

我最終實現了一個新的 VerificationMode 來做到這一點。 這是代碼,以防它對任何人有用。

class MostRecently implements VerificationMode {

    @Override
    public void verify(VerificationData data) {
        MatchableInvocation target = data.getTarget();
        List<Invocation> invocations = data.getAllInvocations();
        if (invocations.isEmpty()) {
            throw wantedButNotInvoked(target);
        }
        List<Invocation> targets = findInvocations(invocations, target);
        if (targets.isEmpty()) {
            throw wantedButNotInvoked(target);
        }
        Invocation lastInvocation = invocations.get(invocations.size() - 1);
        if (target.matches(lastInvocation)) {
            return;
        }
        ListIterator<Invocation> iterator = invocations.listIterator(invocations.size());
        Invocation previous = iterator.previous();
        Invocation undesired = previous;
        while (!target.matches(previous)) {
            undesired = previous;
            previous = iterator.previous();
        }
        Invocation lastGoodInvocation = previous;

        throw new MockitoAssertionError(join(
                "Wanted most recent on '" + lastGoodInvocation.getMock() + "' to be " + lastGoodInvocation.getMethod(),
                "No more interactions wanted after " + lastGoodInvocation.getLocation(),
                "but found this interaction on mock '"
                        + MockUtil.getMockName(undesired.getMock()) + "':",
                undesired.getLocation(),
                allLocations(invocations)));
    }

    static String allLocations(List<Invocation> invocations) {
        StringBuilder sb = new StringBuilder("***\nFor your reference, here is the list of all invocations.\n");

        int counter = 0;
        for (Invocation i : invocations) {
            sb.append(++counter).append(". ");
            sb.append(i.getLocation()).append("\n");
        }

        return sb.toString();
    }
}

暫無
暫無

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

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