使用TestNG测试基于ForkJoinPool的REST服务异步调用
用TestNG测试基于ForkJoinPool的REST服务异步调用逻辑
嘿,我来帮你一步步搞定这个测试!针对你用ForkJoinPool实现的异步调用逻辑,咱们可以从依赖隔离、场景覆盖、结果验证这几个角度来设计TestNG测试用例,下面具体说:
一、准备工作:Mock依赖服务
首先,咱们得把service1和service2给Mock掉,避免测试依赖真实的数据库或外部服务。推荐用Mockito配合TestNG,你只需要在项目里引入对应的依赖包就行。
二、核心测试场景与代码示例
1. 测试正常异步执行场景
这个场景要验证两个任务都被正确提交、执行,并且返回结果符合预期,同时中间的其他操作不会被阻塞。
import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import java.util.Arrays; import java.util.List; import static org.mockito.Mockito.*; import static org.testng.Assert.*; public class ForkJoinAsyncServiceTest { // 被测类(假设你的异步逻辑封装在这个类里) private AsyncServiceUnderTest asyncService; @Mock private Service1 service1; @Mock private Service2 service2; private ForkJoinPool testForkJoinPool; private Filter filter; // 假设你的过滤参数类 @BeforeMethod public void setup() { MockitoAnnotations.openMocks(this); // 创建自定义ForkJoinPool,方便控制并行度,避免测试间干扰 testForkJoinPool = new ForkJoinPool(2); asyncService = new AsyncServiceUnderTest(testForkJoinPool, service1, service2); filter = new Filter(); // 初始化测试用的过滤参数 } @Test public void testAsyncTasksExecuteSuccessfully() { // 1. 准备Mock返回值 List<Something> mockResult1 = Arrays.asList(new Something("item1"), new Something("item2")); List<Something> mockResult2 = Arrays.asList(new Something("item3"), new Something("item4")); when(service1.findAll(filter)).thenReturn(mockResult1); when(service2.findAll(filter)).thenReturn(mockResult2); // 2. 执行被测逻辑 asyncService.executeAsyncTasks(filter); // 3. 等待异步任务完成并获取结果 // 这里假设被测类会保存任务引用,或者你可以通过返回值获取任务实例 ForkJoinTask<List<Something>> task1 = asyncService.getSomeJoinTask1(); ForkJoinTask<List<Something>> task2 = asyncService.getSomeJoinTask2(); List<Something> result1 = task1.join(); List<Something> result2 = task2.join(); // 4. 验证结果与调用行为 assertEquals(result1, mockResult1); assertEquals(result2, mockResult2); verify(service1, times(1)).findAll(filter); verify(service2, times(1)).findAll(filter); // 5. 验证中间的「其他操作」是否正常执行 assertTrue(asyncService.isOtherOperationExecuted()); } }
2. 测试异步任务不阻塞主线程
这个场景要确保提交ForkJoin任务后,主线程能继续执行后续操作,不需要等待任务完成。
import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import org.testng.annotations.Test; import static org.mockito.Mockito.*; import static org.testng.Assert.*; @Test public void testMainThreadNotBlocked() throws InterruptedException { // 用CountDownLatch监听任务完成信号 CountDownLatch taskLatch = new CountDownLatch(2); // Mock service方法,模拟耗时操作 when(service1.findAll(filter)).thenAnswer(invocation -> { Thread.sleep(1000); taskLatch.countDown(); return Arrays.asList(new Something("item1")); }); when(service2.findAll(filter)).thenAnswer(invocation -> { Thread.sleep(1000); taskLatch.countDown(); return Arrays.asList(new Something("item2")); }); // 记录开始时间,验证后续操作是否立即执行 long startTime = System.currentTimeMillis(); asyncService.executeAsyncTasks(filter); long operationTime = System.currentTimeMillis() - startTime; assertTrue(operationTime < 500, "后续操作被异步任务阻塞了"); // 等待任务完成,避免测试提前结束 taskLatch.await(3, TimeUnit.SECONDS); }
3. 测试异常场景
如果某个service调用抛出异常,要验证任务的异常处理逻辑是否符合预期(比如是否捕获异常、是否影响另一个任务)。
@Test(expectedExceptions = RuntimeException.class) public void testTaskFailurePropagates() { // Mock service1抛出异常 when(service1.findAll(filter)).thenThrow(new RuntimeException("Service1 failed")); when(service2.findAll(filter)).thenReturn(Arrays.asList(new Something("item3"))); // 执行被测逻辑 asyncService.executeAsyncTasks(filter); // 获取任务结果,触发异常(TestNG的expectedExceptions会验证这个异常) ForkJoinTask<List<Something>> task1 = asyncService.getSomeJoinTask1(); task1.join(); }
三、关键注意事项
- 自定义ForkJoinPool:测试时尽量用自己创建的ForkJoinPool,不要用默认公共池,避免不同测试间的线程干扰。
- 任务等待方式:可以用
ForkJoinTask.join()阻塞获取结果,或者用ForkJoinPool.awaitQuiescence()等待所有任务结束。 - Mock细节:如果service方法是耗时操作,用
thenAnswer模拟延迟,更贴近真实业务场景。 - 线程安全:如果被测类里的任务引用是共享变量,要注意测试方法间的隔离,避免互相干扰。
内容的提问来源于stack exchange,提问作者Angel
相关产品推荐
相关产品推荐

