簡體   English   中英

如何在 Mockito 中模擬 CompletableFuture 的完成

[英]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.

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