繁体   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