[英]Mockito: method's return value depends on other method called
在我的單元測試中,我需要模擬一個接口,該接口在不同的方法中有nextItem()
和isEmpty()
方法:
public interface MyQueue {
Item nextItem();
boolean isEmpty();
//other methods
...
}
我對mock的要求是isEmpty()
最初應該返回false,但是在nextItem()
, isEmpty()
應該返回true。 因此,我正在用一個項目模擬一個隊列。
nextItem()
第二次,第三次等等會導致特定類型的異常嗎? PS我不想為測試提供我的接口的完整實現,因為其中有其他方法,導致難以理解和冗長的代碼。
您可以使用thenAnswer()實現這一目標,Mockito文檔認為該功能存在爭議:
另一個有爭議的特征,原本沒有包含在Mockito中。 我們建議僅使用toReturn()或toThrow()的簡單存根。 這兩個應該足以測試/測試任何干凈簡單的代碼。
這是答案:
private boolean called = false;
when(mock.nextItem()).thenAnswer(new Answer() {
Object answer(InvocationOnMock invocation) {
called = true;
return item;
}
when(mock.isEmpty()).thenAnswer(new Answer() {
Object answer(InvocationOnMock invocation) {
return called;
}
});
這是一個簡單的例子:
//given
MyQueue mock = mock(MyQueue.class);
given(mock.isEmpty()).willReturn(false, true);
given(mock.nextItem()).willReturn(someItem);
//when
mock.isEmpty(); //yields false
mock.nextItem(); //yields someItem
mock.isEmpty(); //yields true
//then
InOrder inOrder = inOrder(mock);
inOrder.verify(mock).isEmpty();
inOrder.verify(mock).nextItem();
inOrder.verify(mock).isEmpty();
willReturn(false, true)
是指: 返回false
上第一調用和true
上第二 。 InOrder
對象用於驗證調用順序。 更改順序或刪除nextItem()
調用,測試將失敗。
或者,您可以使用以下語法:
given(mock.isEmpty()).
willReturn(false).
willReturn(true).
willThrow(SpecialException.class);
如果你需要更強大的模擬語義,你可以引入重炮 - 自定義答案回調:
given(mock.isEmpty()).willAnswer(new Answer<Boolean>() {
private int counter = 0;
@Override
public Boolean answer(InvocationOnMock invocation) throws Throwable {
switch(++counter) {
case 1: return false;
case 2: return true;
default: throw new SpecialException();
}
}
});
但這很容易導致無法維護的測試代碼,請謹慎使用。
最后,您可以通過僅模擬選定的方法來監視您的真實對象 。
您可以提供一些自定義的答案實施,其中一個取決於另一個:
public class NextItemAnswer implements Answer<Item> {
private int invocationCount = 0;
private Item item;
public NextItemAnswer(Item item) {
this.item = item;
}
public Item answer(InvocationOnMock invocation) throws Throwable {
invocationCount++;
return item;
}
public int getInvocationCount() {
return invocationCount;
}
}
public class IsEmptyAnswer implements Answer<Boolean> {
private NextItemAnswer nextItemAnswer;
public IsEmptyAnswer(NextItemAnswer nextItemAnswer) {
this.nextItemAnswer = nextItemAnswer;
}
public Boolean answer(InvocationOnMock invocation) throws Throwable {
return nextItemAnswer.getInvocationCount() >= 0;
}
}
然后使用它:
NextItemAnswer nextItemAnswer = new NextItemAnswer(item);
IsEmptyAnswer isEmptyAnswer = new IsEmptyAnswer(nextItemAnswer);
when(mock.isEmpty()).thenAnswer(isEmptyAnswer);
when(mock.nextItem()).thenAnswer(nextItemAnswer);
你可能會調整,因為我還沒有測試過這段代碼,但這種方法應該是你需要的。
我確實意識到你明確寫道,你不想提供MyQueue的完整實現,但說實話,這是我要做的第一件事。
實際上,我經常提供相當復雜的接口/對象的“模擬”實現,以便使測試更容易測試。 我並不是唯一一個這樣認為的人:例如,Spring Framework提供了許多模擬版本的復雜對象(MockHttpServletRequest,MockHttpServletResponse等)。
在這種情況下,我會避免使我的測試混亂,並在單獨的包中或甚至在生產代碼中提供此類。
MockQueue會使您的測試比這里給出的其他(但正確的)響應更具可讀性。
你可以告訴mockito使用mockito文檔中描述的技術對連續調用相同的模擬方法進行不同的回答。
when(mock.isEmpty())
.thenReturn(false)
.thenReturn(true);
將使isEmpty()
調用僅在第一次調用時返回true,並且
when(mock.nextItem())
.thenReturn(item)
.thenThrow(new NextOnEmptyQueueException())
將使nextItem()
在第一次調用時返回一些內容,並在以后的調用中拋出異常。
我不知道有可能使其中一種方法的結果依賴於對另一種方法的調用順序。 如果它確實可行,我相信它會變得更加復雜。
您可以制作實用工具方法,然后在任何地方使用它。
public static boolean mockHasInvocation(Object mock, String methodName, Object... args) {
return mockingDetails(mock).getInvocations().stream()
.anyMatch(o -> o.getMethod().getName().equals(methodName) && Arrays.equals(o.getArguments(), args));
}
用法簡單:
if(mockHasInvocation(mockObject, "methodName", "argument1", "argument2")){doSomething();}
在這種情況下,您不需要任何其他變量,它更像是“Mockito風格”。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.