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

如何在Java SpringMVC中实现单端点分批返回测试状态?

实现测试用例实时结果推送的几种方案

针对你在SpringMVC应用中需要实时展示每个测试用例执行结果的需求,下面提供三种可落地的实现方案:


一、Server-Sent Events(SSE)方案

适合后端单向推送结果的场景,无需额外依赖,实现成本低。

核心思路

  1. 定义TestResult实体类,存储测试用例ID、状态(RUNNING/SUCCESS/FAILED)、执行详情、耗时等信息。
  2. 实现单例任务管理类,维护SSE连接会话,异步顺序执行测试用例,完成一个就推送一个结果。
  3. 前端通过SSE长连接接收实时更新,即时渲染UI。

代码示例

任务管理类

@Component
public class TestTaskManager {
    private final Map<String, SseEmitter> emitters = new ConcurrentHashMap<>();
    private final ExecutorService testExecutor = Executors.newSingleThreadExecutor(); // 保证测试顺序执行

    public SseEmitter startTests() {
        SseEmitter emitter = new SseEmitter(0L); // 无超时设置
        String clientId = UUID.randomUUID().toString();
        emitters.put(clientId, emitter);

        testExecutor.submit(() -> {
            List<String> testCases = getTestCaseList(); // 替换为你的30个测试用例标识集合
            for (String testId : testCases) {
                // 推送测试启动状态
                sendUpdate(clientId, new TestResult(testId, "RUNNING", null, 0L));
                try {
                    long startTime = System.currentTimeMillis();
                    // 执行单个测试用例
                    boolean success = executeTestCase(testId);
                    long cost = System.currentTimeMillis() - startTime;
                    // 推送测试完成状态
                    sendUpdate(clientId, new TestResult(testId, success ? "SUCCESS" : "FAILED", "执行完成", cost));
                } catch (Exception e) {
                    sendUpdate(clientId, new TestResult(testId, "FAILED", e.getMessage(), 0L));
                }
            }
            // 全部测试完成后关闭连接
            try {
                emitter.send(SseEmitter.event().name("finish").data("所有测试执行完毕"));
                emitter.complete();
            } catch (IOException e) {
                emitter.completeWithError(e);
            }
            emitters.remove(clientId);
        });

        return emitter;
    }

    private void sendUpdate(String clientId, TestResult result) {
        SseEmitter emitter = emitters.get(clientId);
        if (emitter != null) {
            try {
                emitter.send(SseEmitter.event().name("testUpdate").data(result));
            } catch (IOException e) {
                emitters.remove(clientId);
            }
        }
    }

    // 业务方法:替换为你的测试用例获取逻辑
    private List<String> getTestCaseList() {
        return Arrays.asList("test01", "test02", ..., "test30");
    }

    // 业务方法:替换为你的单个测试用例执行逻辑
    private boolean executeTestCase(String testId) throws Exception {
        // 调用对应测试方法,返回执行结果
        return true;
    }
}

SpringMVC控制器

@RestController
@RequestMapping("/test/api")
public class TestController {
    @Autowired
    private TestTaskManager taskManager;

    @GetMapping("/start")
    public SseEmitter startTests() {
        return taskManager.startTests();
    }
}

前端逻辑

const eventSource = new EventSource('/test/api/start');
// 接收单个测试结果更新
eventSource.addEventListener('testUpdate', (event) => {
    const result = JSON.parse(event.data);
    // 更新UI:比如往结果列表中添加一行记录
    const resultItem = document.createElement('div');
    resultItem.textContent = `${result.testId} | ${result.status} | 耗时${result.cost}ms | ${result.detail}`;
    document.getElementById('test-result-container').appendChild(resultItem);
});
// 接收全部测试完成通知
eventSource.addEventListener('finish', (event) => {
    alert(event.data);
    eventSource.close();
});
// 处理连接错误
eventSource.addEventListener('error', (err) => {
    console.error('SSE连接异常:', err);
    eventSource.close();
});

二、WebSocket双向通信方案

适合需要前端主动交互(比如中途暂停测试)的场景,支持双向消息传递。

核心思路

  1. 引入Spring WebSocket依赖,配置WebSocket端点。
  2. 实现WebSocket处理器,维护前端连接会话,测试用例完成后推送结果。
  3. 前端通过WebSocket建立连接,实时接收结果并可发送控制指令。

关键代码示例

WebSocket配置类

@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
    @Override
    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
        registry.addHandler(testResultHandler(), "/test/ws").setAllowedOrigins("*");
    }

    @Bean
    public WebSocketHandler testResultHandler() {
        return new TestResultWebSocketHandler();
    }
}

WebSocket处理器

public class TestResultWebSocketHandler extends TextWebSocketHandler {
    private static final Set<WebSocketSession> sessions = ConcurrentHashMap.newKeySet();
    private final TestTaskManager taskManager;

    public TestResultWebSocketHandler(TestTaskManager taskManager) {
        this.taskManager = taskManager;
        // 注册测试结果回调
        taskManager.setResultCallback(this::pushResultToFrontend);
    }

    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
        sessions.add(session);
        // 前端连接成功后启动测试任务
        taskManager.startTests();
    }

    private void pushResultToFrontend(TestResult result) {
        String jsonResult = new ObjectMapper().writeValueAsString(result);
        for (WebSocketSession session : sessions) {
            if (session.isOpen()) {
                session.sendMessage(new TextMessage(jsonResult));
            }
        }
    }

    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
        sessions.remove(session);
    }
}

三、轮询+状态存储方案

适合对实时性要求不高、不想维护长连接的场景,实现最简单。

核心思路

  1. 用内存Map(分布式场景用Redis)存储测试任务的实时状态,每个任务生成唯一ID。
  2. 启动测试的接口返回任务ID,前端定期调用查询接口获取已完成的测试结果。
  3. 异步线程执行测试用例,完成一个就更新状态存储。

关键代码示例

控制器方法

@RestController
@RequestMapping("/test/api")
public class TestController {
    @Autowired
    private TestTaskManager taskManager;

    @PostMapping("/start")
    public ResponseEntity<String> startTests() {
        String taskId = taskManager.startTests();
        return ResponseEntity.ok(taskId);
    }

    @GetMapping("/results/{taskId}")
    public ResponseEntity<List<TestResult>> getTestResults(@PathVariable String taskId) {
        return ResponseEntity.ok(taskManager.getTaskResults(taskId));
    }
}

前端轮询逻辑

let taskId;
// 启动测试
fetch('/test/api/start', { method: 'POST' })
    .then(res => res.text())
    .then(id => {
        taskId = id;
        // 每30秒查询一次结果
        setInterval(() => {
            fetch(`/test/api/results/${taskId}`)
                .then(res => res.json())
                .then(results => {
                    // 更新UI:仅渲染新增的测试结果
                    console.log(results);
                });
        }, 30000);
    });

内容的提问来源于stack exchange,提问作者Seriously

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.04 23:07:46