[英]How to mock completion of a CompletableFuture in Mockito
我想模擬當CompletableFuture
成功完成時正在調用一些代碼。
我有這堂課:
public class MyClassImplementRunner implements Runnable {
private final String param1;
public MyClassImplementRunner(String param1) {
this.param1 = param1;
}
public static CompletableFuture<Void> startAsync(String param1) {
return CompletableFuture.runAsync(
new MyClassImplementRunner(param1)).whenComplete(
(response, throwable) -> {
//some code when complete
});
@Override
public void run () {
//the runnable code
}
}
}
在我的 Junit(使用 Mockito 和 Java 8)中,我需要模擬它
//some code when complete
當 Future 成功完成時調用。
您能否就如何實現這一目標提供一些指示?
將您在whenComplete
中執行的代碼提取到一個字段並提供一個構造函數來替換它。
class Runner implement Runnable {
private final String param;
private final BiConsumer<Void, Throwable> callback;
public Runner(String param) {
this.param = param;
this.callback = this::callbackFunction;
}
Runner(String param, BiConsumer<Void, Throwable> callback) {
this.param = param;
this.callback = callback;
}
public void run() {
CompletableFuture.runAsync(task).whenComplete(callback);
}
private void callbackFunction(Void result, Throwable throwable) {
//some code when complete
}
}
測試將如下所示:
class RunnerTest {
@Test
void test() {
new Runner("param", (response, throwable) -> /* mocked behavior */).run();
}
}
我的第一個傾向不是模擬這個:看起來startAsync
是 MyClassImplementRunner 的公共 API 的一部分,你應該一起測試這些部分。 在像 MyClassImplementRunnerTest 這樣的測試類中,將被測系統視為 MyClassImplementRunner 而不會嘗試將其拆分是有意義的。 否則,很容易忘記您正在測試的內容,包括真實內容與模擬內容。
如果 MyClassImplementRunner 正在尋找任何外部條件,您可以模擬該依賴項,這可能會導致您的 CompletableFuture 立即返回; 但是,您只向我們展示了一個字符串參數。
也就是說, startAsync
可能包含您希望在沒有真正的 MyClassImplementRunner 的情況下徹底測試的邏輯。 在這種情況下,您可以為測試創建重載,可能具有有限的可見性或僅測試注釋以指示不應在生產中調用它。
public static CompletableFuture<Void> startAsync(String param1) {
return startAsync(new MyClassImplementRunner(param1);
}
/** Package-private, for a test class in the same package. */
@VisibleForTesting static CompletableFuture<Void> startAsync(Runnable task) {
return CompletableFuture.runAsync(task).whenComplete(
(response, throwable) -> {
//some code when complete
});
}
通過拆分,您現在可以在測試中運行startAsync(new Runnable())
以模擬即時成功的任務,並運行startAsync(() -> { throw new RuntimeException(); })
以模擬即時失敗的任務. 這允許您獨立於 MyClassImplementRunner 測試startAsync
。
為測試重構或引入僅測試方法似乎並不明智,這是一個公平的評估:純粹地說,MyClassImplementRunner應該完全按照消費者運行它的方式進行測試,而不是模擬。 但是,如果您說在測試中使用與 MyClassImplementRunner 不同的 Runnable 運行會更方便,那么您可以控制代碼,並且可以通過在代碼中包含適當的靈活性(“測試接縫”)來為此做好准備你控制。 事實上,如果startAsync
是一個足夠獨立的方法,它可以采用任意 Runnable,那么您可以選擇將其分離為單獨測試的單獨方法。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.