我有以下示例代码。从不同 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时,调用将进入实用程序方法。
多年来一直有一个关于此问题的开放问题 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");
}