如何编写JUnit测试,测量返回DeferredResult的异步方法每次调用执行时间
我来帮你完善这个JUnit测试用例,实现每次调用foo()方法的执行时间测量,同时适配DeferredResult的异步特性~
核心思路说明
因为DeferredResult是异步返回的,不能直接在调用后立刻计算耗时(此时异步逻辑还没完成),所以需要:
- 记录每次调用的开始时间
- 在
DeferredResult完成(成功/超时)时记录结束时间并计算耗时 - 用
CountDownLatch让JUnit主线程等待所有异步操作完成,避免测试提前结束导致数据丢失 - 触发所有
DeferredResult的完成逻辑(对应你提到的“另一个方法遍历集合设置结果”)
完整测试代码
import org.junit.Test; import org.springframework.web.context.request.async.DeferredResult; import java.util.ArrayList; import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; public class AsyncServiceTest { // 实例化你的异步服务类 private AsyncService asyncService = new AsyncService(); // 存储每次调用的执行耗时(毫秒) private final List<Long> executionTimes = new ArrayList<>(); @Test public void executionTimeTest() throws InterruptedException { int totalCalls = 100; // 计数器:等待100次异步操作全部完成 CountDownLatch completionLatch = new CountDownLatch(totalCalls); for (int i = 0; i < totalCalls; i++) { // 记录当前调用的开始时间 long startTime = System.currentTimeMillis(); DeferredResult<String> deferredResult = asyncService.foo(); // 注册完成回调:不管成功还是超时,都计算耗时 deferredResult.onCompletion(() -> { long endTime = System.currentTimeMillis(); long duration = endTime - startTime; executionTimes.add(duration); // 每完成一个异步操作,计数器减1 completionLatch.countDown(); }); } // 触发所有DeferredResult的完成逻辑(替换成你实际的方法) asyncService.resolveAllDeferredResults(); // 等待所有异步操作完成,超时时间设为10秒(可根据实际调整) boolean allCompleted = completionLatch.await(10, TimeUnit.SECONDS); if (!allCompleted) { throw new RuntimeException("部分异步请求未在规定时间内完成"); } // 输出每次调用的耗时 System.out.println("100次调用的执行时间(毫秒):"); for (int i = 0; i < executionTimes.size(); i++) { System.out.printf("第%d次:%dms%n", i + 1, executionTimes.get(i)); } // 可选:统计平均、最大、最小耗时 long totalDuration = executionTimes.stream().mapToLong(Long::longValue).sum(); double avgDuration = totalDuration / (double) totalCalls; long maxDuration = executionTimes.stream().mapToLong(Long::longValue).max().orElse(0); long minDuration = executionTimes.stream().mapToLong(Long::longValue).min().orElse(0); System.out.printf("统计结果:平均耗时%.2fms,最大耗时%dms,最小耗时%dms%n", avgDuration, maxDuration, minDuration); } }
配套补充代码(你的AsyncService类需要添加的方法)
因为测试需要触发DeferredResult的完成,你需要在服务类里添加一个方法来遍历集合设置结果:
// 在你的AsyncService类中添加 public void resolveAllDeferredResults() { // 遍历存储的DeferredResult集合,设置结果 for (DeferredResult dr : deferredResults) { if (!dr.isSetOrExpired()) { dr.setResult("success"); // 替换成你实际要返回的结果 } } // 可选:清空集合,避免后续测试受影响 deferredResults.clear(); }
关键细节说明
- CountDownLatch的必要性:JUnit主线程会在测试方法执行完后立刻结束,而异步回调是在其他线程执行的,用计数器可以强制主线程等待所有异步逻辑完成,确保耗时数据全部收集。
- onCompletion回调的作用:这个回调会在
DeferredResult成功完成或超时触发,能覆盖所有场景下的耗时计算。 - 超时时间设置:
completionLatch.await(10, TimeUnit.SECONDS)中的10秒可以根据你的业务逻辑调整,确保所有请求都能完成。
内容的提问来源于stack exchange,提问作者Denis Stephanov
相关产品推荐
相关产品推荐

