You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何编写JUnit测试,测量返回DeferredResult的异步方法每次调用执行时间

我来帮你完善这个JUnit测试用例,实现每次调用foo()方法的执行时间测量,同时适配DeferredResult的异步特性~

核心思路说明

因为DeferredResult是异步返回的,不能直接在调用后立刻计算耗时(此时异步逻辑还没完成),所以需要:

  1. 记录每次调用的开始时间
  2. 在DeferredResult完成(成功/超时)时记录结束时间并计算耗时
  3. 用CountDownLatch让JUnit主线程等待所有异步操作完成,避免测试提前结束导致数据丢失
  4. 触发所有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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.29 08:50:46