簡體   English   中英

Mockito驗證+任何行為異常

[英]Mockito verify + any behaves unpredictably

我正在為Spring MVC + axon中的控制器編寫集成測試。

我的控制器是一個簡單的RestController,具有以下方法:

@RequestMapping(value = "/", method = RequestMethod.POST)
@ResponseStatus(HttpStatus.CREATED)
public void createEventProposal(@RequestBody CreateEventProposalForm form) {
    CreateEventProposalCommand command = new CreateEventProposalCommand(
            new EventProposalId(),
            form.getName(),
            EventDescription.of(form.getDescription()),
            form.getMinimalInterestThreshold());

    commandGateway.send(command);
}

CreateEventProposalForm只是一個值類,用於從傳入的json收集所有參數。

EventProposalId

是另一個值對象,表示一個標識符。 它可以構造在字符串上,也可以不帶任何參數-在后一種情況下,會生成UUID。

現在,我想編寫一個測試用例,給定適當的json,我的控制器應使用適當的命令對象在命令網關模擬上調用send方法。

這就是mockito表現出不可預測的情況:

@Test
public void givenPostRequestShouldSendAppropriateCommandViaCommandHandler() throws Exception {

    final String jsonString = asJsonString(
            new CreateEventProposalForm(eventProposalName, eventDescription, minimalInterestThreshold)
    );

    mockMvc.perform(
            post(URL_PATH)
                    .contentType(MediaType.APPLICATION_JSON)
                    .content(jsonString)
    );

    verify(commandGatewayMock, times(1))
            .send(
                            new CreateEventProposalCommand(
                                    any(EventProposalId.class),
                                    eventProposalName,
                                    EventDescription.of(eventDescription),
                                    minimalInterestThreshold
                            )
            );


}

如果我將EventProposalId的新實例傳遞給EventProposalCommand構造函數,請說:

new CreateEventProposalCommand(
            EventProposalId.of("anId"),
            eventProposalName,
            EventDescription.of(eventDescription),
            minimalInterestThreshold
    )

失敗了。 但是給定any(EventProposalId.class) ,我可以傳遞像

new CreateEventProposalCommand(
            any(EventProposalId.class),
            "dummy name",
            EventDescription.of("dummy description"),
            666
    )

與其他參數一樣, 測試始終通過。

在沒有方法參數攔截的情況下如何做出這樣的斷言? 這是Mockito的錯誤還是應該以這種方式表現?

我認為錯誤在於

    verify(commandGatewayMock, times(1))
        .send(
                        new CreateEventProposalCommand(
                                any(EventProposalId.class),
                                eventProposalName,
                                EventDescription.of(eventDescription),
                                minimalInterestThreshold
                        )
        );

您實際上是在創建一個新的CreateEventProposalCommand對象,然后將其傳遞給Mockito。 Mockito不會攔截構造函數參數,因此無法使用它們。 在這種情況下, any(EventProposalId.class)僅返回null。 您可以在發送參數中使用匹配器,例如

verify(commandGatewayMock, times(1).send(any(CreateEventProposalCommand.class))

但這當然不符合您的要求。

問題仍然存在:為什么測試總是通過? 我認為這可能是Mockito匹配器的實現細節,在此進行介紹。Mockito匹配器如何工作?

對我來說,它看起來像any()調用以某種方式導致send()匹配任何對象(也許是因為匹配器是“堆疊的”,沒有什么可使用的?),即使它不是故意的。 我寫了一個快速測試,顯示出類似的行為

import org.mockito.Mockito;

public class MockitoTest {

    public void onOuter(Outer outer) {
    }

    public static class Outer {
        private Inner inner;

        public Outer(Inner inner) {
            this.inner = inner;
        }

    }

    public static class Inner {

    }

    public static void main(String[] args) {
        MockitoTest mockitoTest = Mockito.mock(MockitoTest.class);
        mockitoTest.onOuter(new Outer(new Inner()));
        Mockito.verify(mockitoTest)
                .onOuter(new Outer(Mockito.any(Inner.class))); // passes but shouldn't
        Mockito.verify(mockitoTest).onOuter(new Outer(new Inner())); // fails
    }
}

不幸的是,我不知道實現您想要實現的最簡單方法是什么。

為了擴展Paweł的正確答案 ,它之所以通過,是因為在模擬對象上匹配一個參數的方法時,您巧合地使用了一個匹配器,這就是行為不一致的原因。

當你寫:

verify(commandGatewayMock, times(1))
            .send(
                            new CreateEventProposalCommand(
                                    any(EventProposalId.class),
                                    eventProposalName,
                                    EventDescription.of(eventDescription),
                                    minimalInterestThreshold
                            )
            );

... Mockito實際上就像是在匹配:

verify(commandGatewayMock, times(1))
        .send(any());

Mockito匹配器喜歡通過副作用進行的any工作。 調用any不會返回與任何對象匹配的特殊對象實例。 相反,它返回null並告訴Mockito跳過匹配某個參數的操作。 這是如果在存根或驗證中使用任何匹配器,通常都需要對所有參數使用匹配器的部分原因:匹配器和參數必須一對一地對齊,並且Mockito不夠聰明,無法深入使用匹配器(即在對new CreateEventProposalCommand的調用內)。

在這種情況下,Mockito會在堆棧上看到一個any匹配器( any(EventProposalId.class)any上的參數只是為了幫助javac找出泛型)並驗證一個單參數方法( commandGatewayMock.send ),並且錯誤地認為這兩者在一起-這會導致測試通過,無論CreateEventProposalCommand構造函數的參數如何。

暫無
暫無

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

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