2016-12-28 54 views
0

間諜法在單元測試中,我們已經遇到了一個奇怪的錯誤從Mockito,這第一眼看起來微不足道,但經過深入瞭解,我們無法找到它爲什麼有效無效使用的參數匹配的像那樣。請看下面的代碼和拋出的錯誤。打電話時,裏面Matchers.eq

代碼

import org.assertj.core.api.Assertions; 
import org.junit.Assert; 
import org.junit.Before; 
import org.junit.Test; 
import org.junit.runner.RunWith; 
import org.mockito.Answers; 
import org.mockito.ArgumentCaptor; 
import org.mockito.InjectMocks; 
import org.mockito.Matchers; 
import org.mockito.Mock; 
import org.mockito.Mockito; 
import org.mockito.MockitoAnnotations; 
import org.mockito.Spy; 

//... 

@InjectMocks 
@Spy 
private MyWebSocket webSocket; 

@Mock 
private WebSocketUtils webSocketUtils; 

@Test 
public void myTest() throws IOException { 
    WebSocketSession session1 = Mockito.mock(WebSocketSession.class); 
    WebSocketSession session2 = Mockito.mock(WebSocketSession.class); 
    WebSocketSession session3 = Mockito.mock(WebSocketSession.class); 
    webSocket.getUserNameWebSocketSessions().put("user1", session1); 
    webSocket.getUserNameWebSocketSessions().put("user1", session2); 
    webSocket.getUserNameWebSocketSessions().put("user2", session3); 
    Mockito.doReturn(new CustomWebSocketMessage<UserSessionInfo>().config(Command.USER_SESSION_INFO).data(new UserSessionInfo())) 
      .when(webSocket) 
      .buildUserSessionInfoWebSocketMessage(Mockito.any()); 

    webSocket.onUserPermissionsChange(new UserPermissionsChangedEvent(Collections.singletonList("user1"), this)); 

    ArgumentCaptor<CustomWebSocketMessage> messageCaptor = ArgumentCaptor.forClass(CustomWebSocketMessage.class); 
    Mockito.verify(webSocketUtils).sendMessageToMultipleUsers(messageCaptor.capture(), Matchers.eq(webSocket.getUserNameWebSocketSessions().get("user1"))); 
    CustomWebSocketMessage message = messageCaptor.getValue(); 
    Assertions.assertThat(message.getMessagePayload().getConfig().getCommand()).isEqualTo(Command.USER_SESSION_INFO.name()); 
    Assertions.assertThat(message.getMessagePayload().getData()).isInstanceOf(UserSessionInfo.class); 
} 

錯誤

org.mockito.exceptions.misusing.InvalidUseOfMatchersException: 
Invalid use of argument matchers! 
0 matchers expected, 1 recorded: 
-> at ... (Name of the bussines package) 
This exception may occur if matchers are combined with raw values: 
    //incorrect: 
    someMethod(anyObject(), "raw String"); 
When using matchers, all arguments have to be provided by matchers. 
For example: 
    //correct: 
    someMethod(anyObject(), eq("String by matcher")); 

For more info see javadoc for Matchers class. 

正如你所看到的,第一個參數是捕手,第二個是從MockitoMatcher。我不知道爲什麼它不工作,但什麼固定它,是從Matchers.eq()方法讓移動會話變量聲明。

固定

Collection<WebSocketSession> sessions = webSocket.getUserNameWebSocketSessions().get("user1"); 
Mockito.verify(webSocketUtils).sendMessageToMultipleUsers(messageCaptor.capture(), Matchers.eq(sessions)); 

任何人都可以解釋我爲什麼我不得不將它移到一個變量?

回答

1

這是因爲Mockito matchers work via side-effects。這意味着Matcher調用不應該包含複雜的表達式,絕對不應該包含調用Mockito mock或者間諜的表達式。

對於Java等強類型語言,有沒有辦法的Mockito代表或編碼像,看起來像一個int易於區分返回值eq(0)gt(5)anyInt()一個概念,所以的Mockito沒有按別嘗試。相反,的Mockito保持在一個隱藏的疊加狀態,並依靠聰明和微妙的排序方法的調用來操作和查詢它:

  1. 一個調用verify,在對象
  2. 任意數量的電話傳遞給匹配方法
  3. 一個呼叫到的Mockito-嘲笑對象上的方法

when存根具有相似的規則(零個或多個匹配器,一個呼叫的方法在模擬中,一個呼叫到when,一個或多個致電thenVerb),因爲這樣做doVerb存根(一個呼叫到doVerb,零個或多個調用thenVerb,一個呼叫到when,零個或多個匹配器,一個呼叫的方法上的模擬)。但是,Mockito無法在您的測試方法中看到代碼,因此無法知道接下來會發生什麼;它只能通過調用靜態方法或調用模擬方法來看到與Mockito進行交互的時間。這意味着,有時不給你足夠的例外(測試通過錯誤地),有時它給你太多(喜歡這裏),有時它給你錯誤的異常消息或標誌上的錯誤的方法除外。


我會用我在使用的註解慣例如何匹配器的Mockito工作?回答上面鏈接,這說明調用方法前的Java如何評估由左到右的參數表達式。

你的具體情況,你原來的方法遵循以下模式:

Mockito.verify(webSocketUtils).sendMessageToMultipleUsers(
//  [1]     [6] 
    messageCaptor.capture(), 
//    [2] 
    Matchers.eq(webSocket.getUserNameWebSocketSessions().get("user1"))); 
//   [5]   [3]       [4] 
  1. webSocketUtils需求沒有評價,所以verify正確地被稱爲第一
  2. capture就像一個匹配,所以這仍然是正確的因爲是一個模擬調用。哎呦!現在Mockito認爲這是您正在驗證的電話,並且您已經爲無參數方法調用準備好了匹配器。看起來像匹配器的無效使用!

此時執行停止,所以你甚至不會打電話給你的第二個匹配器[5]或你打算模擬的方法[6]。

一旦你解決它:

Collection<WebSocketSession> sessions = 
    webSocket.getUserNameWebSocketSessions().get("user1"); 
// [1] 
Mockito.verify(webSocketUtils).sendMessageToMultipleUsers(
//  [2]     [5] 
    messageCaptor.capture(), Matchers.eq(sessions)); 
//    [3]     [4] 
  1. 呼叫到的Mockito控制對象發生,因爲如果你的系統下測試使他們完全一樣。
  2. verify發生在正確的時間。
  3. capture將一個匹配器放入堆棧。
  4. eq將另一個匹配器放入堆棧。
  5. 對Mockito模擬的調用發生,完成模式。 Mockito使用兩個匹配器對兩個參數執行驗證。