簡體   English   中英

不能在驗證參數中使用模擬函數調用:調用太多

[英]Cannot use mocked function calls in parameters of verify: too many invocations

設置如下:

//call doA a bunch of times, call doB once using some value that depends on doA()
verify(mockedThing).doB(eq(mockedThing.doA())); //removing eq() changes nothing

顯然, doA()被配置為返回一些值,而mockedThing確實是被mockedThing 其結果是:抱怨的Mockito我叫doA (這里強調:NOT doB !)過於頻繁,而且它預計它只能調用一次!

以下更改有效:

int result = mockedThing.doA() 
verify(mockedThing).doB(eq(result));

我的問題很簡單:這里發生了什么? 為什么 Mockito 驗證對我傳遞給函數的參數的調用而不是對函數本身的調用?

正如我在評論中提到的,Mockito 實際上是以不直觀的方式有狀態的; 多次方法存根或驗證簡直是“最后調用的方法”,主要是因為像語法verify(foo).doA()實際上調用doA ,而不是傳遞給方法的反射參考doA到的Mockito。 這與在存根或驗證過程中調用相同模擬的語法不兼容。

我之前寫過關於 Matchers 的文章,它在存根期間有同樣的問題。 翻閱源代碼,你可以看到驗證同樣的問題,至少在同一個模擬上調用方法時是這樣。

簡而言之,驗證實際上是一個三階段的過程:

  1. 調用verify(mockedThing)
  2. 如有必要,按順序調用匹配器。 不要在mockedThing上調用任何方法。
  3. 調用您在mockedThing驗證的方法,如果您不使用匹配器,則使用實際參數值,如果您使用匹配器,則使用虛擬(忽略)參數值。 由於 Mockito 在后台跟蹤匹配器堆棧,匹配器方法可以返回0null而 Mockito 不會認為這些是要檢查的值。

在封面下

調用verify實際上只是設置一個標志並返回完全相同的模擬

public <T> T verify(T mock, VerificationMode mode) {
  // [catch errors]
  mockingProgress.verificationStarted(new MockAwareVerificationMode(mock, mode));
  return mock;
}

然后, 在處理所有模擬調用的處理程序,Mockito 在第一次調用模擬時開始驗證,一旦開始驗證,就會發生這種情況:

public Object handle(Invocation invocation) throws Throwable {
  // [detect doAnswer stubbing]
  VerificationMode verificationMode = mockingProgress.pullVerificationMode();
  // [check Matcher state]

  // if verificationMode is not null then someone is doing verify()
  if (verificationMode != null) {
    // We need to check if verification was started on the correct mock
    // - see VerifyingWithAnExtraCallToADifferentMockTest (bug 138)
    if (((MockAwareVerificationMode) verificationMode).getMock() == invocation.getMock()) {
      VerificationDataImpl data = createVerificationData(invocationContainerImpl, invocationMatcher);
      verificationMode.verify(data);
      return null;
    } else {
      // this means there is an invocation on a different mock. Re-adding verification mode
      // - see VerifyingWithAnExtraCallToADifferentMockTest (bug 138)
      mockingProgress.verificationStarted(verificationMode);
    }
  }

  // [prepare invocation for stubbing]
}

因此,如果您與模擬交互只是為了獲取參數值,Mockito 將假定您實際上是在調用方法進行驗證。 請注意,如果調用略有不同,例如verify(mockedThing).doB(eq(5), eq(mockedThing.doA())); 與額外的eq(5)你會得到關於濫用的匹配,特別是因為它的Mockito不只是覺得你驗證不同的錯誤信息doA ,但你會莫名其妙地認為doA需要一個參數。

結果

您的代碼不起作用:

// DOESN'T WORK
verify(mockedThing).doB(eq(mockedThing.doA()));
// BECAUSE IT BEHAVES THE SAME AS
verify(mockedThing).doA();

但提取它確實有效:

// WORKS, though it makes an extra call to doA
Value value = mockedThing.doA();
verify(mockedThing).doB(eq(value));

這也有效,並展示了幕后發生的事情,但永遠不要在真正的測試中寫這個

// WORKS BUT DON'T EVER ACTUALLY DO THIS
Value value = mockedThing.doA();
verify(mockedThing);
eq(value);
mockedThing.doB(8675309 /* dummy value ignored because of matcher */);

傑夫鮑曼的回答有助於解釋發生了什么。

許多 Mockito 都基於最后調用的函數,並且對模擬的調用以與您嘗試使用的語法不兼容的方式隱式地檢查狀態。 我在這里寫了更多關於這個答案的內容。 – 傑夫鮑曼昨天

暫無
暫無

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

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