通过OpenAPI 测试脚本生成器批量生成脚本
1. 概述
OpenAPI 测试生成器 会根据 OpenAPI 3.x 规范生成 API 测试脚本,并将生成出来的测试用例直接注册到 Testany。
当前实现已经切换到与 Import from Git V2 相同的托管 Git mirror 基础模型。使用 OpenAPI Generate 时,您不需要再配置 GitHub 连接、仓库 URL、分支或仓库 Token。
主要优势:
- 快速生成: 根据 OpenAPI 契约批量生成正向、正向边界和反向测试脚本,显著减少初始编写工作量。
- 平台托管存储: 生成产物由 Testany 托管到内部 mirror 中,用户不再为这条流程单独维护目标 Git 仓库。
- 生成记录可追踪: 每次生成都归属于一个生成记录,后续可基于该记录继续更新、复用或删除。
- 覆盖模型标准化: 生成用例遵循统一的场景模型。完整的场景清单请参阅附录。
2. 开始之前
2.1 准备有效的 OpenAPI 文件
- 当前 UI 使用 本地文件上传。
- 支持 OpenAPI 3.x 及以上版本。
- 强烈建议先使用 42Crunch 等 API 质量或安全工具对规范进行审计,确保生成基于稳定且可执行的 API 契约。
2.2 选择可用的运行时
- 每个生成记录都绑定一个 运行时。
- 该运行时就是后续执行生成脚本的目标运行环境。
- 当前不可用或不健康的运行时会在界面中置灰,无法被选中。
2.3 规划生成记录
- 生成记录
Name必须唯一,且创建后不可修改。 - 后续更新已有记录时,会继续沿用该记录已有的运行时。
- 生成记录是生成用例生命周期的锚点,后续的更新和删除行为都围绕该记录发生。
3. 创建生成记录
3.1 进入向导
3.2 创建新记录
在 步骤 1/2 中,点击 创建新生成记录。
3.2.1 配置记录
在 步骤 2/2 中,填写:
- 名称: 生成记录名称,创建后不可修改。
- 运行时: 用于执行生成脚本的目标运行时。
- OpenAPI 文件: 上传本地 OpenAPI 文件。
文件解析成功后,Testany 会显示确认信息,并允许您继续进入端点选择步骤。
3.2.2 选择端点并生成
解析完成后:
- 检查已识别出的 API 端点。
- 选择您要生成的端点或路径分组。
- 点击 生成测试脚本。
生成成功后,成功弹窗会显示已选择端点数量和生成脚本总数,并提供跳转到测试用例库的入口。
3.2.3 查看生成出的测试用例
生成完成后:
3.2.4 理解生成脚本实际存放在哪里
生成的脚本和配套文件会在后台保存到 Testany 托管的 mirror 仓库 中。
这意味着:
- 向导里不再有仓库 URL 字段
- 向导里不再有分支字段
- 向导里不再有仓库凭证字段
从底层实现上看,这条链路已经与 Git V2 的 mirror 模型对齐;但从用户操作上看,OpenAPI Generate 的入口更简单,不需要您先完成额外的 Git 连接配置。
4. 更新或删除生成记录
4.1 更新已有记录
更新已有记录的步骤是:
- 在 步骤 1/2 中选择已有生成记录。
- 上传新的本地 OpenAPI 文件。
- 检查解析结果,并重新选择需要生成的端点。
更新记录时:
- 记录原有的名称和运行时保持不变
- 当前选中的端点会基于最新上传的文件重新生成
- 之前生成过、但现在被取消选择或已从新版 OpenAPI 中消失的端点,会根据当前使用情况进行处理
对于这些被移除的端点:
- 如果对应测试用例没有被任何流水线使用,该用例会被删除
- 如果对应测试用例已经被某条流水线使用,平台会保留该用例以避免破坏流水线,但会解除它与生成记录之间的关联
这类“被保留但已解除关联”的用例,如果其原先对应的生成脚本已经从托管 mirror 中移除,后续执行可能失败。更新后请尽快检查并替换这些用例。
4.2 删除生成记录
删除生成记录后,平台会移除该记录与其生成用例之间的关联。
删除后的行为如下:
- 未被任何测试流水线组装的用例会被永久删除
- 已被流水线组装的用例会被保留,但会解除与该生成记录的关联
如果您希望这些被保留的用例也一并删除,请先把它们从流水线中移除,再回到测试用例库手动删除。
由于这条流程不再使用用户自有的 Git 仓库,所以删除记录后也不存在需要您手工清理的外部分支或仓库。
5. 常见问题 (FAQ)
❓ 问题: 为什么现在看不到仓库 URL、分支和仓库 Token 这些字段了?
💡 回答: 因为 OpenAPI 测试生成器现在已经改为使用 Testany 托管的内部 mirror 仓库。平台负责这条流程的脚本存储和同步,因此终端用户不再需要在向导中提供 Git 仓库信息。
❓ 问题: 如果我把某个端点取消选择,或者它在新版 OpenAPI 文件里已经不存在了,会发生什么?
💡 回答: 如果对应测试用例没有被任何流水线使用,它会被删除;如果它已经被某条流水线使用,平台会保留该用例以保证流水线完整性,但会移除它与当前生成记录之间的关联。
❓ 问题: 为什么有些生成出来的测试用例删不掉?
💡 回答: 已经被一条或多条流水线组装的生成用例,会受到同样的“流水线完整性保护”规则约束。如果您希望永久删除它们,请先把它们从流水线中移除。
❓ 问题: 现在应该怎样更新生成结果?
💡 回答: 复用同一个生成记录,重新上传本地 OpenAPI 文件,并重新选择需要生成的端点即可。不要再按“编辑用户自有 Git 分支”的思路来规划这条流程,因为它已经不再依赖那种模型。
附录
下表列出了Testany生成过程中所有支持的测试场景。对于每个场景,表格详细说明了:
- 场景ID:测试场景的唯一标识符
- 字段类型:场景适用的数据类型
- 场景:正向、负面和正向边界测试用例的描述
- 必需约束:必须定义的OpenAPI规范约束
- 跳过条件:不生成该场景的情况
| 场景 ID | 字段类型 | 场景描述 | 必要约束条件 | 跳过条件 |
|---|---|---|---|---|
| 1.00 | string | 符合所有约束条件的有效字符串值 | ||
| 1.01 | string | 不符合指定格式的字符串值 | 定义了 format | |
| 1.02 | string | 不符合指定模式的字符串值 | 定义了 enum | |
| 1.03 | string | 字符串长度小于 minLength | 定义了 minLength | 定义了 enum |
| 1.04 | string | minLength > 0 时,字符串为空 | minLength > 0 | 定义了 enum |
| 1.05 | string | 字符串长度大于 maxLength | 定义了 enum | |
| 1.06 | string | 字符串值不在 enum 列表中 | 定义了 enum | |
| 1.07 | string | nullable 为 false 时,字符串值为 null | nullable=false | |
| 1.08 | string | 值类型是数字而不是字符串 | type=string | |
| 1.09 | string | 值类型是布尔值而不是字符串 | type=string | |
| 1.10 | string | 值类型是数组而不是字符串 | type=string | |
| 1.11 | string | 值类型是对象而不是字符串 | type=string | |
| 1.21 | string | payload 中不存在此字段 | 字段是必需的 | |
| 1.22 | string | 字符串长度刚好等于 minLength | 定义了 minLength | 定义了 enum |
| 1.23 | string | 字符串长度刚好等于 maxLength | 定义了 maxLength | 定义了 enum |
| 2.00 | integer | 符合所有约束条件的有效整数值 | ||
| 2.01 | integer | 不符合指定格式的整数值 | 定义了 format | 定义了 enum |
| 2.03 | integer | 整数值小于 minimum | 定义了 minimum | 定义了 enum |
| 2.05 | integer | 整数值大于 maximum | 定义了 maximum | 定义了 enum |
| 2.06 | integer | 整数值不在 enum 列表中 | 定义了 enum | 定义了 minimum、maximum、multipleOf中的任意一个 |
| 2.07 | integer | nullable 为 false 时,整数值为 null | nullable=false | |
| 2.09 | integer | 值类型是布尔值而不是整数 | 定义了 format | |
| 2.10 | integer | 值类型是数组而不是整数 | 定义了 format | |
| 2.11 | integer | 值类型是对象而不是整数 | 定义了 format | |
| 2.12 | integer | 值类型是浮点数而不是整数 | 定义了 format | |
| 2.13 | integer | exclusiveMinimum 为 true 时,整数值等于 minimum | exclusiveMinimum=true | 定义了 enum |
| 2.15 | integer | exclusiveMaximum 为 true 时,整数值等于 maximum | exclusiveMaximum=true | 定义了 enum |
| 2.16 | integer | 整数值不是指定数字的倍数 | 定义了 multipleOf | 定义了 enum、minimum、maximum中的任意一个 |
| 2.17 | integer | 值类型是字符串而不是整数 | type=integer | |
| 2.21 | integer | payload 中不存在此字段 | 字段是必需的 | |
| 2.22 | integer | 整数值刚好等于 minimum | 定义了 minimum | 定义了 enum |
| 2.23 | integer | 整数值刚好等于 maximum | 定义了 maximum | 定义了 enum |
| 2.24 | integer | 整数值刚好大于 minimum | exclusiveMinimum=true | 定义了 enum |
| 2.25 | integer | 整数值刚好小于 maximum | exclusiveMaximum=true | 定义了 enum |
| 3.00 | boolean | 符合所有约束条件的有效布尔值 | ||
| 3.08 | boolean | 值类型是数字而不是布尔值 | type=boolean | |
| 3.10 | boolean | 值类型是数组而不是布尔值 | type=boolean | |
| 3.11 | boolean | 值类型是对象而不是布尔值 | type=boolean | |
| 3.17 | boolean | 值类型是字符串而不是布尔值 | type=boolean | |
| 3.21 | boolean | payload 中不存在此字段 | 字段是必需的 | |
| 4.00 | number | 符合所有约束条件的有效数值 | ||
| 4.01 | number | 不符合指定格式的数值 | 定义了 format | 定义了 enum |
| 4.03 | number | 数值小于 minimum | 定义了 minimum | 定义了 enum |
| 4.05 | number | 数值大于 maximum | 定义了 maximum | 定义了 enum |
| 4.06 | number | 数值不在 enum 列表中 | 定义了 enum | 定义了 minimum、maximum、multipleOf中的任意一个 |
| 4.07 | number | nullable 为 false 时,数值为 null | nullable=false | |
| 4.09 | number | 值类型是布尔值而不是数值 | type=number | |
| 4.10 | number | 值类型是数组而不是数值 | type=number | |
| 4.11 | number | 值类型是对象而不是数值 | type=number | |
| 4.13 | number | exclusiveMinimum 为 true 时,数值等于 minimum | exclusiveMinimum=true | 定义了 enum |
| 4.15 | number | exclusiveMaximum 为 true 时,数值等于 maximum | exclusiveMaximum=true | 定义了 enum |
| 4.16 | number | 数值不是指定数字的倍数 | 定义了 multipleOf | 定义了 enum、minimum、maximum中的任意一个 |
| 4.17 | number | 值类型是字符串而不是数值 | type=number | |
| 4.21 | number | payload 中不存在此字段 | 字段是必需的 | |
| 4.22 | number | 数值刚好等于 minimum | 定义了 minimum | 定义了 enum |
| 4.23 | number | 数值刚好等于 maximum | 定义了 maximum | 定义了 enum |
| 4.24 | number | 数值刚好大于 minimum | exclusiveMinimum=true | 定义了 enum |
| 4.25 | number | 数值刚好小于 maximum | exclusiveMaximum=true | 定义了 enum |
| 5.00 | array | 符合所有约束条件的有效数组值 | ||
| 5.03 | array | 数组长度小于 minItems | 定义了 minItems | |
| 5.04 | array | minItems > 0 时,数组为空 | minItems > 0 | |
| 5.05 | array | 数组长度大于 maxItems | ||
| 5.07 | array | nullable 为 false 时,数组值为 null | nullable=false | |
| 5.08 | array | 值类型是数字而不是数组 | type=array | |
| 5.09 | array | 值类型是布尔值而不是数组 | type=array | |
| 5.11 | array | 值类型是对象而不是数组 | type=array | |
| 5.17 | array | 值类型是字符串而不是数组 | type=array | |
| 5.18 | array | uniqueItems 为 true 时,数组包含重复项 | uniqueItems=true | |
| 5.21 | array | payload 中不存在此字段 | 字段是必需的 | |
| 6.00 | object | 符合所有约束条件的有效对象值 | ||
| 6.03 | object | 对象属性数量小于 minProperties | 定义了 minProperties | |
| 6.04 | object | minProperties > 0 时,对象为空 | minProperties > 0 | |
| 6.05 | object | 对象属性数量大于 maxProperties | 定义了 maxProperties | |
| 6.07 | object | nullable 为 false 时,对象值为 null | nullable=false | |
| 6.08 | object | 值类型是数字而不是对象 | type=object | |
| 6.09 | object | 值类型是布尔值而不是对象 | type=object | |
| 6.10 | object | 值类型是数组而不是对象 | type=object | |
| 6.17 | object | 值类型是字符串而不是对象 | type=object | |
| 6.19 | object | 对象缺少必需属性 | 属性是必需的 | |
| 6.20 | object | additionalProperties 为 false 时,对象包含额外属性 | additionalProperties=false | |
| 6.21 | object | payload 中不存在此字段 | 字段是必需的 | |
| 6.22 | object | 对象属性数量刚好等于 minProperties | 定义了 minProperties | |
| 6.23 | object | 对象属性数量刚好等于 maxProperties | 定义了 maxProperties |
以 test_post_pet_petId_negative_body_status.py为例:
如果 POST /pet/{petId} 请求体中 status 字段的 OpenAPI 定义是带有特定约束(例如,一个 enum)的字符串,则生成的脚本 test_post_pet_petId_negative_body_status.py 将包含根据这些约束而适用的上述表格中的场景测试。例如,如果 status 被定义为一个字符串枚举,那么场景 1.02(不符合模式,如果定义了模式)、1.05(长度大于 maxLength,如果定义了 maxLength)、1.06(不在 enum 列表中),以及类型不匹配(1.08、1.09、1.10、1.11)等都可能被生成,前提是存在相关的约束。
还有疑问?
我们的团队随时为您服务。欢迎联系我们,我们会尽快回复。