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

如何将Selenium Java测试结果同步至Azure DevOps测试计划?

解决Java Selenium测试结果同步到Azure DevOps测试计划的方案

针对你用Java Selenium执行测试、需要同步结果(状态+截图)到Azure DevOps手动测试用例的需求,直接通过Azure DevOps REST API实现的步骤如下:

1. 准备必要信息与权限

  • 个人访问令牌(PAT):在Azure DevOps个人设置中创建,权限需勾选Test Management的Read & Write。
  • 关键ID:
    • 测试计划ID:从测试计划页面URL提取(如.../TestPlans/Edit?planId=123中的123)
    • 测试套件ID:测试套件页面URL中的suiteId参数值
    • 手动测试用例ID:每个目标用例在Azure DevOps详情页显示的ID

2. Java中API请求的基础配置

用JDK11+自带的HttpClient发送请求,请求头需包含认证信息:

import java.util.Base64;

// PAT前加冒号再做Base64编码,生成认证头
String pat = "你的PAT令牌";
String authHeader = "Basic " + Base64.getEncoder().encodeToString((":".concat(pat)).getBytes());

3. 创建测试运行

先创建关联目标测试计划和套件的测试运行,后续结果都归属到该运行下:

import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;

public class AzureDevOpsSync {
    private static final String ORG = "你的组织名";
    private static final String PROJ = "你的项目名";
    private static final String API_VERSION = "7.1-preview.3";
    private static final HttpClient client = HttpClient.newHttpClient();

    public static String createTestRun(String authHeader, int planId, int suiteId) throws Exception {
        String url = String.format("https://dev.azure.com/%s/%s/_apis/test/runs?api-version=%s", ORG, PROJ, API_VERSION);
        String requestBody = String.format("""
            {
                "name": "Selenium自动化测试运行-%s",
                "plan": {"id": %d},
                "suite": {"id": %d},
                "isAutomated": false
            }""", System.currentTimeMillis(), planId, suiteId);

        HttpRequest request = HttpRequest.newBuilder()
                .uri(URI.create(url))
                .header("Authorization", authHeader)
                .header("Content-Type", "application/json")
                .POST(HttpRequest.BodyPublishers.ofString(requestBody, StandardCharsets.UTF_8))
                .build();

        HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
        // 简化解析测试运行ID,实际建议用Jackson/Gson等JSON库
        return response.body().split("\"id\":")[1].split(",")[0].trim();
    }
}

4. 获取测试点ID(关联手动用例必需)

每个手动用例在测试套件中对应一个测试点,需获取其ID来绑定结果:

public static String getTestPointId(String authHeader, int planId, int suiteId, int testCaseId) throws Exception {
    String url = String.format("https://dev.azure.com/%s/%s/_apis/test/plans/%d/suites/%d/testpoints?api-version=7.1-preview.2&testCaseId=%d",
            ORG, PROJ, planId, suiteId, testCaseId);

    HttpRequest request = HttpRequest.newBuilder()
            .uri(URI.create(url))
            .header("Authorization", authHeader)
            .GET()
            .build();

    HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
    return response.body().split("\"id\":")[1].split(",")[0].trim();
}

5. 更新测试结果并上传截图

创建测试结果标记成功/失败状态,再上传截图作为附件:

// 创建测试结果
public static String createTestResult(String authHeader, String runId, int testCaseId, String testPointId, boolean isPassed) throws Exception {
    String url = String.format("https://dev.azure.com/%s/%s/_apis/test/runs/%s/results?api-version=7.1-preview.6", ORG, PROJ, runId);
    String outcome = isPassed ? "Passed" : "Failed";
    String requestBody = String.format("""
        [
            {
                "testCaseId": %d,
                "testPoint": {"id": %s},
                "outcome": "%s",
                "comment": "Selenium自动化测试执行结果"
            }
        ]""", testCaseId, testPointId, outcome);

    HttpRequest request = HttpRequest.newBuilder()
            .uri(URI.create(url))
            .header("Authorization", authHeader)
            .header("Content-Type", "application/json")
            .POST(HttpRequest.BodyPublishers.ofString(requestBody, StandardCharsets.UTF_8))
            .build();

    HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
    return response.body().split("\"id\":")[1].split(",")[0].trim();
}

// 上传截图附件
public static void uploadScreenshot(String authHeader, String runId, String resultId, String screenshotPath) throws Exception {
    String url = String.format("https://dev.azure.com/%s/%s/_apis/test/runs/%s/results/%s/attachments?api-version=7.1-preview.1",
            ORG, PROJ, runId, resultId);
    byte[] screenshotBytes = java.nio.file.Files.readAllBytes(java.nio.file.Paths.get(screenshotPath));
    
    String boundary = "----WebKitFormBoundary7MA4YWxkTrZu0gW";
    String body = String.format("""
            --%s
            Content-Disposition: form-data; name="Attachment"; filename="screenshot.png"
            Content-Type: image/png

            %s
            --%s--""", boundary, new String(screenshotBytes, StandardCharsets.ISO_8859_1), boundary);

    HttpRequest request = HttpRequest.newBuilder()
            .uri(URI.create(url))
            .header("Authorization", authHeader)
            .header("Content-Type", "multipart/form-data; boundary=" + boundary)
            .POST(HttpRequest.BodyPublishers.ofString(body, StandardCharsets.ISO_8859_1))
            .build();

    client.send(request, HttpResponse.BodyHandlers.discarding());
}

6. 整合执行流程

在Selenium测试结束后,调用上述方法完成同步:

public static void main(String[] args) {
    try {
        String authHeader = "Basic " + Base64.getEncoder().encodeToString((":你的PAT").getBytes());
        int planId = 123;
        int suiteId = 456;
        int testCaseId = 789;
        String screenshotPath = "测试失败截图的本地路径";
        boolean testPassed = false; // 根据实际测试结果设置

        String runId = createTestRun(authHeader, planId, suiteId);
        String testPointId = getTestPointId(authHeader, planId, suiteId, testCaseId);
        String resultId = createTestResult(authHeader, runId, testCaseId, testPointId, testPassed);
        
        // 仅测试失败时上传截图,可根据需求调整
        if (!testPassed) {
            uploadScreenshot(authHeader, runId, resultId, screenshotPath);
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
}

注意事项

  • 实际项目建议用Jackson/Gson等JSON库解析API响应,避免字符串分割的脆弱性
  • 流水线中执行时,将PAT作为秘密变量存储,禁止硬编码
  • 检查API响应状态码:401代表PAT权限不足,404代表输入的ID不存在

内容的提问来源于stack exchange,提问作者Ahmed Alaa Ewis

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.01 20:45:08