模拟 Java CompletableFuture.supplyAsync [重复]

问题描述 投票:0回答:1

我有以下示例代码。从不同 Completable Future 中的实用程序类调用不同的静态方法,并将它们连接起来以获得结果。

但是,在编写 JUnit 时,即使我正在执行静态模拟,调用也会进入实用程序类内部。如果我删除 Completable Future 实现,JUnits 可以与模拟一起正常工作。

我有什么遗漏或者需要以不同的方式模拟 Completable Future 吗?

我的代码:

完整的未来代码:

package com.demo.futures;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;

public class CompletableFutureSample {

    public List<String> myShortTaskWithoutCompletableFuture() {

        System.out.println("Inside myShortTaskWithoutCompletableFuture");

        List<String> res = new ArrayList<>();

        res.add(MyUtils1.getSampleValue1("Ranesh"));

        return res;
    }

    public List<String> myShortTask() {

        System.out.println("Inside myShortTask");

        List<CompletableFuture<String>> futures = new ArrayList<>();
        List<String> res = new ArrayList<>();

        CompletableFuture<String> future1 = CompletableFuture
                .supplyAsync(() -> MyUtils1.getSampleValue1("Ranesh"));

        futures.add(future1);

        try {
            CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();
            for (CompletableFuture<String> future : futures) {
                res.add(future.get());
            }
        } catch (InterruptedException | ExecutionException e) {
            System.out.println(e.getMessage());
        }

        return res;
    }

    public List<String> myTask() {

        List<CompletableFuture<String>> futures = new ArrayList<>();
        List<String> res = new ArrayList<>();

        CompletableFuture<String> future1 = CompletableFuture
                .supplyAsync(() -> MyUtils1.getSampleValue1("Ranesh"));

        CompletableFuture<String> future2 = CompletableFuture
                .supplyAsync(() -> MyUtils2.getSampleValue2("Ranesh"));

        CompletableFuture<String> future3 = CompletableFuture
                .supplyAsync(() -> MyUtils3.getSampleValue3("Ranesh"));

        futures.add(future1);
        futures.add(future2);
        futures.add(future3);

        try {
            CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();
            for (CompletableFuture<String> future : futures) {
                res.add(future.get());
            }
        } catch (InterruptedException | ExecutionException e) {
            System.out.println(e.getMessage());
        }

        return res;
    }

}

实用类:

package com.demo.futures;

public class MyUtils1 {

    public static String getSampleValue1(String str) {
        System.out.println("Inside My Utils 1");
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        return "Hello : " + str;
    }

}

我的 JUnit 类:

package com.demo.futures;

import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.MockedStatic;
import org.mockito.Mockito;
import org.mockito.junit.jupiter.MockitoExtension;

import java.util.List;

import static org.mockito.ArgumentMatchers.anyString;

@ExtendWith(MockitoExtension.class)
class CompletableFutureSampleTest {

    @InjectMocks
    private CompletableFutureSample completableFutureSample;

    private MockedStatic<MyUtils1> myUtils1MockedStatic;

    @BeforeEach
    void setup() {
        completableFutureSample = new CompletableFutureSample();
        myUtils1MockedStatic = Mockito.mockStatic(MyUtils1.class);
    }

    @Test
    void testMyShortTaskWithoutCompletableFuture() {

        myUtils1MockedStatic.when(() -> MyUtils1.getSampleValue1(anyString())).thenReturn("Test String Result");
        List<String> res = completableFutureSample.myShortTaskWithoutCompletableFuture();
        Assertions.assertEquals(1, res.size());
        Assertions.assertEquals("Test String Result", res.get(0));

    }

    @Test
    void testMyShortTask() {

        myUtils1MockedStatic.when(() -> MyUtils1.getSampleValue1(anyString())).thenReturn("Test String Result");
        List<String> res = completableFutureSample.myShortTask();
        Assertions.assertEquals(1, res.size());
        Assertions.assertEquals("Test String Result", res.get(0));

    }

    @Test
    void testMyTask() {

    }

    @AfterEach
    void cleanUp() {
        myUtils1MockedStatic.close();
    }

}

执行测试:testMyShortTask时,调用将进入实用程序方法。

java junit java-8 mocking completable-future
1个回答
0
投票

多年来一直有一个关于此问题的开放问题 ISSUE-2142

本质上存根静态资源在主线程之外将不可用。

存根静态/私有资源总是会有一些限制。如果可以,请尝试注入您的依赖项。 CompletableFuture 默认在 ForkJoin 池上运行,并且可能会导致线程饥饿,具体取决于您的上下文。好的做法是注入一个 ExecutorService,您可以更好地控制它,并且如果绝对需要,可以更轻松地模拟。

public String myShortTask() throws ExecutionException, InterruptedException {

    return myExecutor.submit(() -> MyUtils1.getSampleValue1("Ranesh")).get();
}
@Test
void testMyShortTask() {

    when(executorService.submit((Callable<String>) any())).thenAnswer(invocation -> {
        final Callable<?> callable = invocation.getArgument(0);
        var value = callable.call();
        return CompletableFuture.completedFuture(value);
    });

    myUtils1MockedStatic.when(() -> MyUtils1.getSampleValue1(anyString())).thenReturn("Test String Result");

    String res = completableFutureSample.myShortTask();

    Assertions.assertEquals("Test String Result");
}
© www.soinside.com 2019 - 2024. All rights reserved.