为了账号安全,请及时绑定邮箱和手机立即绑定

如何在 Mockito 中模拟 CompletableFuture 的完成

如何在 Mockito 中模拟 CompletableFuture 的完成

波斯汪 2021-12-10 15:40:29
我想模拟当 aCompletableFuture成功完成时正在调用一些代码。我有这门课: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 成功完成时调用。您能否提供一些有关如何实现这一目标的指示?
查看完整描述

2 回答

?
潇潇雨雨

TA贡献1833条经验 获得超4个赞

将您在其中执行的代码提取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();

  }

}


查看完整回答
反对 回复 2021-12-10
?
手掌心

TA贡献1942条经验 获得超3个赞

我的第一个倾向不是嘲笑这个:它看起来像是startAsyncMyClassImplementRunner 公共 API 的一部分,你应该一起测试这些部分。在像 MyClassImplementRunnerTest 这样的测试类中,将被测系统视为 MyClassImplementRunner 而不试图将其拆分是有意义的。否则,很容易忘记您正在测试的内容,包括什么是真实的,什么是模拟的。


如果存在MyClassImplementRunner 正在寻找的任何外部条件,您可以模拟该依赖项,这可能会导致您的 CompletableFuture 立即返回;但是,您只向我们展示了一个 String 参数。


也就是说,可能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(); })以模拟立即失败的任务。这允许您startAsync独立于 MyClassImplementRunner进行测试。


为测试而重构或引入仅测试方法似乎并不明智,这是一个公平的评估:纯粹来说,MyClassImplementRunner应该完全按照消费者运行它的方式进行测试,而不是模拟。但是,如果您说在测试中使用与 MyClassImplementRunner 不同的 Runnable 运行更方便,则您可以控制代码,并且可以通过在代码中包含适当的灵活性(“测试接缝”)来为此做好准备你控制。事实上,如果startAsync是一个足够独立的方法,它可以带一个任意的Runnable,你可以选择把它分离出来作为一个单独的方法,单独测试。


查看完整回答
反对 回复 2021-12-10
  • 2 回答
  • 0 关注
  • 1022 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信