# API 文档 - 测试执行 ## 元数据 - 需求编号:002-03 - 需求名称:测试执行 - 创建时间:2026-02-03 - 创建人:AI Assistant - 版本:v1.0.0 - 状态:已完成 ## API 概述 本文档描述了 Salesforce Apex 测试执行功能的 RESTful API 接口。这些接口提供了运行 Apex 测试、查询测试结果、查看代码覆盖率和 Flow 覆盖率等功能。 ### 核心功能 1. **测试执行**:支持运行所有测试、按类运行、按包运行、按方法运行 2. **结果查询**:支持分页查询历史测试结果 3. **详情查询**:支持查询测试成功、失败、覆盖率等详细信息 4. **覆盖率分析**:支持查询代码覆盖率和 Flow 覆盖率 ### 技术栈 - **框架**:Spring Boot 2.7.x - **文档**:Swagger 3.0 (OpenAPI 3.0) - **权限**:Spring Security + @PreAuthorize - **基础路径**:`/api/apex/test` ### 权限说明 所有接口都需要相应的权限才能访问: | 权限标识 | 说明 | 适用接口 | |----------|------|----------| | `apex:test:execute` | 测试执行权限 | run-all、run-by-classes、run-by-packages、run-by-methods | | `apex:test:query` | 测试查询权限 | results、details、code-coverage、flow-coverage | ## 接口列表 ### 1. 运行所有测试 **接口名称**:运行所有测试 **功能描述**:运行 Salesforce 组织中的所有 Apex 测试类 **请求方式**:POST **请求路径**:`/api/apex/test/run-all` **权限要求**:`apex:test:execute` **请求参数**: | 参数名 | 类型 | 必填 | 说明 | 示例 | |--------|------|------|------|------| | skipCodeCoverage | Boolean | 否 | 是否跳过代码覆盖率检查 | false | | maxFailedTests | Integer | 否 | 最大失败测试数,0 表示不限制 | 0 | **请求示例**: ```json { "skipCodeCoverage": false, "maxFailedTests": 0 } ``` **响应参数**: | 参数名 | 类型 | 说明 | |--------|------|------| | code | Integer | 状态码(200 成功,其他失败) | | msg | String | 提示信息 | | data | Object | 测试结果数据 | | data.numTestsRun | Integer | 运行的测试数量 | | data.numFailures | Integer | 失败的测试数量 | | data.totalTime | Double | 总执行时间(毫秒) | | data.successes | Array | 成功的测试列表 | | data.failures | Array | 失败的测试列表 | | data.codeCoverage | Array | 代码覆盖率结果列表 | | data.flowCoverage | Array | Flow 覆盖率结果列表 | | data.apexLogId | String | Apex 日志 ID | **成功响应示例**: ```json { "code": 200, "msg": "操作成功", "data": { "numTestsRun": 100, "numFailures": 2, "totalTime": 12345.67, "successes": [ { "name": "AccountTest.testCreate", "time": 123.45, "methodName": "testCreate", "className": "AccountTest" } ], "failures": [ { "name": "AccountTest.testUpdate", "message": "System.AssertException: Assertion Failed", "methodName": "testUpdate", "className": "AccountTest", "stackTrace": "Class.AccountTest.testUpdate: line 15, column 1", "type": "System.AssertException" } ], "codeCoverage": [ { "name": "AccountTest", "type": "Class", "numLocations": 100, "numLocationsNotCovered": 20, "coveragePercent": 80.0 } ], "flowCoverage": [], "apexLogId": "07Lxx0000003DHb2AAG" } } ``` **失败响应示例**: ```json { "code": 500, "msg": "运行所有测试失败: ConnectionException: Invalid session ID" } ``` --- ### 2. 运行指定类的测试 **接口名称**:运行指定类的测试 **功能描述**:运行指定 Apex 类的所有测试方法 **请求方式**:POST **请求路径**:`/api/apex/test/run-by-classes` **权限要求**:`apex:test:execute` **请求参数**: | 参数名 | 类型 | 必填 | 说明 | 示例 | |--------|------|------|------|------| | classes | Array[String] | 是 | 要测试的类名称数组 | ["AccountTest", "ContactTest"] | | skipCodeCoverage | Boolean | 否 | 是否跳过代码覆盖率检查 | false | | maxFailedTests | Integer | 否 | 最大失败测试数 | 0 | **请求示例**: ```json { "classes": ["AccountTest", "ContactTest"], "skipCodeCoverage": false, "maxFailedTests": 0 } ``` **响应参数**:同「运行所有测试」接口 **成功响应示例**: ```json { "code": 200, "msg": "操作成功", "data": { "numTestsRun": 20, "numFailures": 0, "totalTime": 2345.67, "successes": [ { "name": "AccountTest.testCreate", "time": 123.45, "methodName": "testCreate", "className": "AccountTest" } ], "failures": [], "codeCoverage": [ { "name": "AccountTest", "type": "Class", "numLocations": 100, "numLocationsNotCovered": 20, "coveragePercent": 80.0 } ], "flowCoverage": [], "apexLogId": "07Lxx0000003DHb2AAG" } } ``` --- ### 3. 运行指定包的测试 **接口名称**:运行指定包的测试 **功能描述**:运行指定命名空间包中的所有 Apex 测试 **请求方式**:POST **请求路径**:`/api/apex/test/run-by-packages` **权限要求**:`apex:test:execute` **请求参数**: | 参数名 | 类型 | 必填 | 说明 | 示例 | |--------|------|------|------|------| | namespace | String | 否 | 命名空间 | "datai" | | packages | Array[String] | 是 | 要测试的包数组 | ["com.datai.test"] | | skipCodeCoverage | Boolean | 否 | 是否跳过代码覆盖率检查 | false | | maxFailedTests | Integer | 否 | 最大失败测试数 | 0 | **请求示例**: ```json { "namespace": "datai", "packages": ["com.datai.test"], "skipCodeCoverage": false, "maxFailedTests": 0 } ``` **响应参数**:同「运行所有测试」接口 --- ### 4. 运行指定测试方法 **接口名称**:运行指定测试方法 **功能描述**:运行指定的 Apex 测试方法 **请求方式**:POST **请求路径**:`/api/apex/test/run-by-methods` **权限要求**:`apex:test:execute` **请求参数**: | 参数名 | 类型 | 必填 | 说明 | 示例 | |--------|------|------|------|------| | tests | Array[Object] | 是 | 测试方法数组 | 见示例 | | tests[].className | String | 是 | 测试类名称 | "AccountTest" | | tests[].testMethods | Array[String] | 是 | 测试方法名称数组 | ["testCreate", "testUpdate"] | | skipCodeCoverage | Boolean | 否 | 是否跳过代码覆盖率检查 | false | | maxFailedTests | Integer | 否 | 最大失败测试数 | 0 | **请求示例**: ```json { "tests": [ { "className": "AccountTest", "testMethods": ["testCreate", "testUpdate", "testDelete"] }, { "className": "ContactTest", "testMethods": ["testCreate", "testUpdate"] } ], "skipCodeCoverage": false, "maxFailedTests": 0 } ``` **响应参数**:同「运行所有测试」接口 --- ### 5. 查询测试结果列表 **接口名称**:查询测试结果列表 **功能描述**:分页查询历史测试结果 **请求方式**:GET **请求路径**:`/api/apex/test/results` **权限要求**:`apex:test:query` **请求参数**: | 参数名 | 类型 | 必填 | 说明 | 示例 | |--------|------|------|------|------| | testRunId | String | 否 | 测试运行 ID | "707xx0000003DHb2AAG" | | startTime | String | 否 | 开始时间(yyyy-MM-dd HH:mm:ss) | "2026-01-28 10:00:00" | | endTime | String | 否 | 结束时间(yyyy-MM-dd HH:mm:ss) | "2026-01-28 12:00:00" | | pageNum | Integer | 否 | 页码,默认为 1 | 1 | | pageSize | Integer | 否 | 每页大小,默认为 10 | 20 | **请求示例**: ``` GET /api/apex/test/results?pageNum=1&pageSize=20 ``` **响应参数**: | 参数名 | 类型 | 说明 | |--------|------|------| | code | Integer | 状态码 | | msg | String | 提示信息 | | data | Object | 分页数据 | | data.total | Integer | 总记录数 | | data.rows | Array | 测试结果列表 | | data.rows[].id | Long | 记录 ID | | data.rows[].testRunId | String | 测试运行 ID | | data.rows[].numTestsRun | Integer | 运行的测试数量 | | data.rows[].numFailures | Integer | 失败的测试数量 | | data.rows[].totalTime | Double | 总执行时间 | | data.rows[].apexLogId | String | Apex 日志 ID | | data.rows[].testTime | String | 测试时间 | | data.rows[].createTime | String | 创建时间 | **成功响应示例**: ```json { "code": 200, "msg": "操作成功", "data": { "total": 50, "rows": [ { "id": 1, "testRunId": "707xx0000003DHb2AAG", "numTestsRun": 10, "numFailures": 0, "totalTime": 1234.56, "apexLogId": "07Lxx0000003DHb2AAG", "testTime": "2026-01-28 10:00:00", "createTime": "2026-01-28 10:00:00" } ] } } ``` --- ### 6. 查询测试详情 **接口名称**:查询测试详情 **功能描述**:查询指定测试结果的详细信息,包括成功、失败、覆盖率等 **请求方式**:GET **请求路径**:`/api/apex/test/details` **权限要求**:`apex:test:query` **请求参数**: | 参数名 | 类型 | 必填 | 说明 | 示例 | |--------|------|------|------|------| | testResultId | Long | 是 | 测试结果 ID | 1 | | type | String | 否 | 类型(success/failure/all),默认为 "all" | "all" | | pageNum | Integer | 否 | 页码,默认为 1 | 1 | | pageSize | Integer | 否 | 每页大小,默认为 10 | 20 | **请求示例**: ``` GET /api/apex/test/details?testResultId=1&type=all&pageNum=1&pageSize=20 ``` **响应参数**: | 参数名 | 类型 | 说明 | |--------|------|------| | code | Integer | 状态码 | | msg | String | 提示信息 | | data | Object | 测试详情数据 | | data.successes | Array | 成功的测试列表 | | data.successes[].id | Long | 记录 ID | | data.successes[].testResultId | Long | 测试结果 ID | | data.successes[].className | String | 类名 | | data.successes[].methodName | String | 方法名 | | data.successes[].time | Double | 执行时间 | | data.failures | Array | 失败的测试列表 | | data.failures[].id | Long | 记录 ID | | data.failures[].testResultId | Long | 测试结果 ID | | data.failures[].className | String | 类名 | | data.failures[].methodName | String | 方法名 | | data.failures[].message | String | 失败消息 | | data.failures[].stackTrace | String | 堆栈跟踪 | | data.failures[].type | String | 异常类型 | **成功响应示例**: ```json { "code": 200, "msg": "操作成功", "data": { "successes": [ { "id": 1, "testResultId": 1, "className": "AccountTest", "methodName": "testCreate", "time": 123.45, "createTime": "2026-01-28 10:00:00" } ], "failures": [ { "id": 2, "testResultId": 1, "className": "AccountTest", "methodName": "testUpdate", "message": "System.AssertException: Assertion Failed", "stackTrace": "Class.AccountTest.testUpdate: line 15, column 1", "type": "System.AssertException", "createTime": "2026-01-28 10:00:00" } ] } } ``` --- ### 7. 查询代码覆盖率 **接口名称**:查询代码覆盖率 **功能描述**:查询指定测试结果的代码覆盖率 **请求方式**:GET **请求路径**:`/api/apex/test/code-coverage` **权限要求**:`apex:test:query` **请求参数**: | 参数名 | 类型 | 必填 | 说明 | 示例 | |--------|------|------|------|------| | testResultId | Long | 否 | 测试结果 ID | 1 | | name | String | 否 | 类/触发器名称 | "AccountTest" | | type | String | 否 | 类型(Class/Trigger) | "Class" | | minCoverage | Double | 否 | 最小覆盖率 | 75.0 | | maxCoverage | Double | 否 | 最大覆盖率 | 100.0 | | pageNum | Integer | 否 | 页码,默认为 1 | 1 | | pageSize | Integer | 否 | 每页大小,默认为 10 | 20 | **请求示例**: ``` GET /api/apex/test/code-coverage?type=Class&minCoverage=75&pageNum=1&pageSize=20 ``` **响应参数**: | 参数名 | 类型 | 说明 | |--------|------|------| | code | Integer | 状态码 | | msg | String | 提示信息 | | data | Object | 分页数据 | | data.total | Integer | 总记录数 | | data.rows | Array | 代码覆盖率列表 | | data.rows[].id | Long | 记录 ID | | data.rows[].testResultId | Long | 测试结果 ID | | data.rows[].name | String | 类/触发器名称 | | data.rows[].type | String | 类型(Class/Trigger) | | data.rows[].namespace | String | 命名空间 | | data.rows[].numLocations | Integer | 总位置数 | | data.rows[].numLocationsNotCovered | Integer | 未覆盖位置数 | | data.rows[].coveragePercent | Double | 覆盖率百分比 | | data.rows[].createTime | String | 创建时间 | **成功响应示例**: ```json { "code": 200, "msg": "操作成功", "data": { "total": 30, "rows": [ { "id": 1, "testResultId": 1, "name": "AccountTest", "type": "Class", "namespace": null, "numLocations": 100, "numLocationsNotCovered": 20, "coveragePercent": 80.0, "createTime": "2026-01-28 10:00:00" } ] } } ``` --- ### 8. 查询 Flow 覆盖率 **接口名称**:查询 Flow 覆盖率 **功能描述**:查询指定测试结果的 Flow 覆盖率 **请求方式**:GET **请求路径**:`/api/apex/test/flow-coverage` **权限要求**:`apex:test:query` **请求参数**: | 参数名 | 类型 | 必填 | 说明 | 示例 | |--------|------|------|------|------| | testResultId | Long | 否 | 测试结果 ID | 1 | | name | String | 否 | Flow 名称 | "Account_Flow" | | type | String | 否 | Flow 类型 | "RecordTriggeredFlow" | | minCoverage | Double | 否 | 最小覆盖率 | 75.0 | | maxCoverage | Double | 否 | 最大覆盖率 | 100.0 | | pageNum | Integer | 否 | 页码,默认为 1 | 1 | | pageSize | Integer | 否 | 每页大小,默认为 10 | 20 | **请求示例**: ``` GET /api/apex/test/flow-coverage?pageNum=1&pageSize=20 ``` **响应参数**: | 参数名 | 类型 | 说明 | |--------|------|------| | code | Integer | 状态码 | | msg | String | 提示信息 | | data | Object | 分页数据 | | data.total | Integer | 总记录数 | | data.rows | Array | Flow 覆盖率列表 | | data.rows[].id | Long | 记录 ID | | data.rows[].testResultId | Long | 测试结果 ID | | data.rows[].name | String | Flow 名称 | | data.rows[].type | String | Flow 类型 | | data.rows[].namespace | String | 命名空间 | | data.rows[].numElements | Integer | 总元素数量 | | data.rows[].numElementsCovered | Integer | 覆盖的元素数量 | | data.rows[].coveragePercent | Double | 覆盖率百分比 | | data.rows[].createTime | String | 创建时间 | **成功响应示例**: ```json { "code": 200, "msg": "操作成功", "data": { "total": 10, "rows": [ { "id": 1, "testResultId": 1, "name": "Account_Flow", "type": "RecordTriggeredFlow", "namespace": null, "numElements": 50, "numElementsCovered": 40, "coveragePercent": 80.0, "createTime": "2026-01-28 10:00:00" } ] } } ``` ## 错误码 ### 系统错误码 | 错误码 | 错误消息 | 说明 | HTTP 状态码 | |--------|----------|------|-------------| | 200 | 操作成功 | 请求处理成功 | 200 | | 500 | 操作失败 | 服务器内部错误 | 500 | | 401 | 未授权 | 用户未登录或 Token 无效 | 401 | | 403 | 禁止访问 | 用户没有权限访问该资源 | 403 | | 404 | 资源不存在 | 请求的资源不存在 | 404 | | 400 | 请求参数错误 | 请求参数不合法 | 400 | ### 业务错误码 | 错误码 | 错误消息 | 说明 | HTTP 状态码 | |--------|----------|------|-------------| | APEX_TEST_001 | 测试请求为空 | 测试请求参数不能为空 | 400 | | APEX_TEST_002 | 测试类为空 | 测试类名称数组不能为空 | 400 | | APEX_TEST_003 | 测试包为空 | 测试包数组不能为空 | 400 | | APEX_TEST_004 | 测试方法为空 | 测试方法数组不能为空 | 400 | | APEX_TEST_005 | 测试结果 ID 为空 | 测试结果 ID 不能为空 | 400 | | APEX_TEST_006 | 连接失败 | 无法建立与 Salesforce 的连接 | 500 | | APEX_TEST_007 | 测试执行失败 | Apex 测试执行失败 | 500 | | APEX_TEST_008 | 测试结果保存失败 | 测试结果保存到数据库失败 | 500 | | APEX_TEST_009 | 代码覆盖率保存失败 | 代码覆盖率保存到数据库失败 | 500 | | APEX_TEST_010 | Flow 覆盖率保存失败 | Flow 覆盖率保存到数据库失败 | 500 | | APEX_TEST_011 | 测试结果不存在 | 指定的测试结果不存在 | 404 | | APEX_TEST_012 | 测试结果查询失败 | 查询测试结果失败 | 500 | | APEX_TEST_013 | 测试详情查询失败 | 查询测试详情失败 | 500 | | APEX_TEST_014 | 覆盖率查询失败 | 查询覆盖率失败 | 500 | ### 错误响应示例 **参数错误**: ```json { "code": 400, "msg": "测试类不能为空", "data": null } ``` **权限不足**: ```json { "code": 403, "msg": "访问权限不足", "data": null } ``` **服务器错误**: ```json { "code": 500, "msg": "测试执行失败: ConnectionException: Invalid session ID", "data": null } ``` ## 数据模型 ### 测试结果 (RunTestsResultVo) | 字段名 | 类型 | 说明 | |--------|------|------| | numTestsRun | Integer | 运行的测试数量 | | numFailures | Integer | 失败的测试数量 | | totalTime | Double | 总执行时间(毫秒) | | successes | List | 成功的测试列表 | | failures | List | 失败的测试列表 | | codeCoverage | List | 代码覆盖率列表 | | codeCoverageWarnings | List | 代码覆盖率警告列表 | | flowCoverage | List | Flow 覆盖率列表 | | flowCoverageWarnings | List | Flow 覆盖率警告列表 | | apexLogId | String | Apex 日志 ID | ### 测试成功详情 (RunTestSuccessVo) | 字段名 | 类型 | 说明 | |--------|------|------| | name | String | 测试名称 | | time | Double | 执行时间(毫秒) | | methodName | String | 方法名称 | | className | String | 类名称 | ### 测试失败详情 (RunTestFailureVo) | 字段名 | 类型 | 说明 | |--------|------|------| | name | String | 测试名称 | | message | String | 失败消息 | | methodName | String | 方法名称 | | className | String | 类名称 | | stackTrace | String | 堆栈跟踪 | | type | String | 异常类型 | ### 代码覆盖率结果 (CodeCoverageResultVo) | 字段名 | 类型 | 说明 | |--------|------|------| | name | String | 类/触发器名称 | | type | String | 类型(Class/Trigger) | | namespace | String | 命名空间 | | numLocations | Integer | 总位置数 | | numLocationsNotCovered | Integer | 未覆盖位置数 | | coveragePercent | Double | 覆盖率百分比 | ### Flow 覆盖率结果 (FlowCoverageResultVo) | 字段名 | 类型 | 说明 | |--------|------|------| | name | String | Flow 名称 | | type | String | Flow 类型 | | namespace | String | 命名空间 | | numElements | Integer | 总元素数量 | | numElementsCovered | Integer | 覆盖的元素数量 | | coveragePercent | Double | 覆盖率百分比 | ## 相关文档 - [需求文档](../requirements/sub/2026-01-28-002-03-测试执行.md) - [设计文档](../design/2026-02-02-002-03-测试执行-设计.md) - [决策记录](../decisions/2026-02-02-002-03-ADR-测试执行技术选型.md) - [变更日志](../changelog/2026-02-02-002-03-changelog.md) - [复盘文档](../retros/2026-02-02-002-03-retro.md) - [会话记录](../sessions/2026-02-02-002-03-session.md) --- **文档版本**:v1.0.0 **最后更新时间**:2026-02-03 **维护人**:AI Assistant