猿问

如何在 Mockito 中模拟 CompletableFuture 的完成

我想模拟当 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 成功完成时调用。


您能否提供一些有关如何实现这一目标的指示?


波斯汪
浏览 1238回答 2
2回答

潇潇雨雨

将您在其中执行的代码提取whenComplete到一个字段中,并提供一个构造函数来替换它。class Runner implement Runnable {&nbsp; private final String param;&nbsp; private final BiConsumer<Void, Throwable> callback;&nbsp; public Runner(String param) {&nbsp; &nbsp; this.param = param;&nbsp; &nbsp; this.callback = this::callbackFunction;&nbsp; }&nbsp; Runner(String param, BiConsumer<Void, Throwable> callback) {&nbsp; &nbsp; this.param = param;&nbsp; &nbsp; this.callback = callback;&nbsp; }&nbsp; public void run() {&nbsp; &nbsp; CompletableFuture.runAsync(task).whenComplete(callback);&nbsp; }&nbsp; private void callbackFunction(Void result, Throwable throwable) {&nbsp; &nbsp; //some code when complete&nbsp; }}测试将如下所示:class RunnerTest {&nbsp; @Test&nbsp; void test() {&nbsp; &nbsp; new Runner("param", (response, throwable) -> /* mocked behavior */).run();&nbsp; }}

手掌心

我的第一个倾向不是嘲笑这个:它看起来像是startAsyncMyClassImplementRunner 公共 API 的一部分,你应该一起测试这些部分。在像 MyClassImplementRunnerTest 这样的测试类中,将被测系统视为 MyClassImplementRunner 而不试图将其拆分是有意义的。否则,很容易忘记您正在测试的内容,包括什么是真实的,什么是模拟的。如果存在MyClassImplementRunner 正在寻找的任何外部条件,您可以模拟该依赖项,这可能会导致您的 CompletableFuture 立即返回;但是,您只向我们展示了一个 String 参数。也就是说,可能startAsync包含您想要在没有真正 MyClassImplementRunner 的情况下进行详尽测试的逻辑。在这种情况下,您可以创建用于测试的重载,可能具有有限的可见性或仅用于测试的注释,以指示不应在生产中调用它。public static CompletableFuture<Void> startAsync(String param1) {&nbsp; return startAsync(new MyClassImplementRunner(param1);}/** Package-private, for a test class in the same package. */@VisibleForTesting static CompletableFuture<Void> startAsync(Runnable task) {&nbsp; return CompletableFuture.runAsync(task).whenComplete(&nbsp; &nbsp; &nbsp; (response, throwable) -> {&nbsp; &nbsp; &nbsp; &nbsp; //some code when complete&nbsp; &nbsp; });}通过将其拆分,您现在可以startAsync(new Runnable())在测试中运行以模拟立即成功的任务,并运行startAsync(() -> { throw new RuntimeException(); })以模拟立即失败的任务。这允许您startAsync独立于 MyClassImplementRunner进行测试。为测试而重构或引入仅测试方法似乎并不明智,这是一个公平的评估:纯粹来说,MyClassImplementRunner应该完全按照消费者运行它的方式进行测试,而不是模拟。但是,如果您说在测试中使用与 MyClassImplementRunner 不同的 Runnable 运行更方便,则您可以控制代码,并且可以通过在代码中包含适当的灵活性(“测试接缝”)来为此做好准备你控制。事实上,如果startAsync是一个足够独立的方法,它可以带一个任意的Runnable,你可以选择把它分离出来作为一个单独的方法,单独测试。
随时随地看视频慕课网APP

相关分类

Java
我要回答