[英]How to mock void methods with Mockito
如何模擬具有 void 返回類型的方法?
我實現了一個觀察者模式,但我不能用 Mockito 模擬它,因為我不知道怎么做。
我試圖在互聯網上找到一個例子但沒有成功。
我的課看起來像這樣:
public class World {
List<Listener> listeners;
void addListener(Listener item) {
listeners.add(item);
}
void doAction(Action goal,Object obj) {
setState("i received");
goal.doAction(obj);
setState("i finished");
}
private string state;
//setter getter state
}
public class WorldTest implements Listener {
@Test public void word{
World w= mock(World.class);
w.addListener(this);
...
...
}
}
interface Listener {
void doAction();
}
系統不會被模擬觸發。
我想顯示上述系統狀態。 並據此作出斷言。
查看 Mockito API 文檔。 正如鏈接文檔中提到的(第 12 點),您可以使用doThrow()
任何doThrow()
、 doAnswer()
、 doNothing()
、 doReturn()
系列方法來模擬 void 方法。
例如,
Mockito.doThrow(new Exception()).when(instance).methodName();
或者如果你想把它與后續行為結合起來,
Mockito.doThrow(new Exception()).doNothing().when(instance).methodName();
假設您正在考慮在下面的類 World 中模擬設置器setState(String s)
是代碼使用doAnswer
方法來模擬setState
。
World mockWorld = mock(World.class);
doAnswer(new Answer<Void>() {
public Void answer(InvocationOnMock invocation) {
Object[] args = invocation.getArguments();
System.out.println("called with arguments: " + Arrays.toString(args));
return null;
}
}).when(mockWorld).setState(anyString());
我想我已經找到了一個更簡單的答案,只為一個方法調用真正的方法(即使它有一個 void 返回),你可以這樣做:
Mockito.doCallRealMethod().when(<objectInstance>).<method>();
<objectInstance>.<method>();
或者,您可以為該類的所有方法調用真正的方法,這樣做:
<Object> <objectInstance> = mock(<Object>.class, Mockito.CALLS_REAL_METHODS);
補充一下@sateesh 所說的,當您只想模擬一個 void 方法以防止測試調用它時,您可以這樣使用Spy
:
World world = new World();
World spy = Mockito.spy(world);
Mockito.doNothing().when(spy).methodToMock();
當你想運行你的測試時,確保你在spy
對象上而不是在world
對象上調用 test 中的方法。 例如:
assertEquals(0, spy.methodToTestThatShouldReturnZero());
所謂問題的解決方案是使用spy
Mockito.spy(...)而不是mock
Mockito.mock(..) 。
Spy 使我們能夠進行部分模擬。 Mockito 擅長這件事。 因為您的課程不完整,因此您可以通過這種方式模擬該課程中的某些必需位置。
首先:您應該始終導入 mockito static,這樣代碼將更具可讀性(和直觀):
import static org.mockito.Mockito.*;
對於部分模擬並仍然保留其余部分的原始功能,mockito 提供“間諜”。
您可以按如下方式使用它:
private World world = spy(new World());
要消除正在執行的方法,您可以使用以下內容:
doNothing().when(someObject).someMethod(anyObject());
為方法使用“when”和“thenReturn”提供一些自定義行為:
doReturn("something").when(this.world).someMethod(anyObject());
有關更多示例,請在文檔中找到優秀的 mockito 示例。
如何使用 mockito 模擬 void 方法 - 有兩種選擇:
doAnswer
- 如果我們希望我們的模擬 void 方法做某事(盡管無效,但模擬行為)。doThrow
- 如果您想從Mockito.doThrow()
的 void 方法中拋出異常,則可以使用Mockito.doThrow()
。以下是如何使用它的示例(不是理想的用例,只是想說明基本用法)。
@Test
public void testUpdate() {
doAnswer(new Answer<Void>() {
@Override
public Void answer(InvocationOnMock invocation) throws Throwable {
Object[] arguments = invocation.getArguments();
if (arguments != null && arguments.length > 1 && arguments[0] != null && arguments[1] != null) {
Customer customer = (Customer) arguments[0];
String email = (String) arguments[1];
customer.setEmail(email);
}
return null;
}
}).when(daoMock).updateEmail(any(Customer.class), any(String.class));
// calling the method under test
Customer customer = service.changeEmail("old@test.com", "new@test.com");
//some asserts
assertThat(customer, is(notNullValue()));
assertThat(customer.getEmail(), is(equalTo("new@test.com")));
}
@Test(expected = RuntimeException.class)
public void testUpdate_throwsException() {
doThrow(RuntimeException.class).when(daoMock).updateEmail(any(Customer.class), any(String.class));
// calling the method under test
Customer customer = service.changeEmail("old@test.com", "new@test.com");
}
}
您可以在我的文章How to mock with Mockito(帶示例的綜合指南)中找到有關如何使用 Mockito模擬和測試void方法的更多詳細信息
在 Java 8 中,這可以更org.mockito.Mockito.doAnswer
一些,假設你有一個org.mockito.Mockito.doAnswer
的靜態導入:
doAnswer(i -> {
// Do stuff with i.getArguments() here
return null;
}).when(*mock*).*method*(*methodArguments*);
return null;
很重要,如果沒有它,編譯將失敗並出現一些相當模糊的錯誤,因為它將無法為doAnswer
找到合適的覆蓋。
例如,一個ExecutorService
立即執行傳遞給execute()
任何Runnable
可以使用以下方法實現:
doAnswer(i -> {
((Runnable) i.getArguments()[0]).run();
return null;
}).when(executor).execute(any());
添加另一個答案到一堆(沒有雙關語意)...
如果您不能\\不想使用間諜,則確實需要調用 doAnswer 方法。 但是,您不一定需要推出自己的Answer 。 有幾個默認實現。 值得注意的是, CallsRealMethods 。
在實踐中,它看起來像這樣:
doAnswer(new CallsRealMethods()).when(mock)
.voidMethod(any(SomeParamClass.class));
或者:
doAnswer(Answers.CALLS_REAL_METHODS.get()).when(mock)
.voidMethod(any(SomeParamClass.class));
我認為您的問題是由於您的測試結構造成的。 我發現很難將模擬與在測試類中實現接口的傳統方法混合使用(正如您在此處所做的那樣)。
如果將偵聽器實現為 Mock,則可以驗證交互。
Listener listener = mock(Listener.class);
w.addListener(listener);
world.doAction(..);
verify(listener).doAction();
這應該讓您滿意,“世界”正在做正確的事情。
如果您需要在模擬的void方法中進行一些操作,並且您需要操作發送給void方法的參數; 您可以將 Mockito.doAnswer 與 ArgumentCaptor.capture 方法結合使用。
假設您有一個自動裝配GalaxyService的SpaceService ,它有一個名為someServiceMethod的 void 方法。
您想為SpaceService中調用GalaxyService的 void 方法的方法之一編寫測試。 你的星球也在 SpaceService 中生成。 所以你沒有任何機會嘲笑它。
這是您要為其編寫測試的示例SpaceService類。
class SpaceService {
@Autowired
private GalaxyService galaxyService;
public Date someCoolSpaceServiceMethod() {
// does something
Planet planet = new World();
galaxyService.someServiceMethod(planet); //Planet updated in this method.
return planet.getCurrentTime();
}
}
GalaxyService.someServiceMethod方法需要一個 planet 參數。 在方法中做一些事情。 看 :
GalaxyService {
public void someServiceMethod(Planet planet) {
//do fancy stuff here. about solar system etc.
planet.setTime(someCalculatedTime); // the thing that we want to test.
// some more stuff.
}
}
並且您想測試此功能。
這是一個例子:
ArgumentCaptor<World> worldCaptor = ArgumentCaptor.forClass(World.class);
Date testDate = new Date();
Mockito.doAnswer(mocked-> {
World capturedWorld = worldCaptor.getValue();
world.updateTime(testDate);
return null;
}).when(galaxyService.someServiceMethod(worldCaptor.capture());
Date result = spaceService.someCoolSpaceServiceMethod();
assertEquals(result, testDate);
根據不同的場景,可以通過2種方式來完成:
doAnswer-如果我們希望模擬的void方法做某事(盡管無效,也要模擬行為)。
doThrow-如果您想從模擬的void方法拋出異常,我們也有Mockito.doThrow()。
在您的示例中,您應該模擬 Listener 項目並使用 Mockito.verify 檢查與它的交互
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.