@raina-npm/opentest 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/skills/opentest/SKILL.md +46 -0
- package/.claude/skills/opentest/references/artifact-templates.md +130 -0
- package/.claude/skills/opentest/references/commands-reference.md +85 -0
- package/.claude/skills/opentest/scripts/init_opentest.py +47 -0
- package/.claude/skills/opentest-apply/SKILL.md +20 -0
- package/.claude/skills/opentest-archive/SKILL.md +27 -0
- package/.claude/skills/opentest-design/SKILL.md +18 -0
- package/.claude/skills/opentest-generate/SKILL.md +75 -0
- package/.claude/skills/opentest-generate/requirements.txt +2 -0
- package/.claude/skills/opentest-generate/scripts/generate_excel.py +84 -0
- package/.claude/skills/opentest-generate/scripts/generate_xmind.py +101 -0
- package/.claude/skills/opentest-new/SKILL.md +33 -0
- package/.claude/skills/opentest-report/SKILL.md +32 -0
- package/.claude/skills/opentest-scope/SKILL.md +33 -0
- package/.claude/skills/opentest-spec/SKILL.md +37 -0
- package/.claude/skills/opentest-tasks/SKILL.md +18 -0
- package/README.md +91 -0
- package/bin/cli.js +386 -0
- package/bin/init.js +83 -0
- package/package.json +32 -0
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: opentest
|
|
3
|
+
description: OpenTest 流程总览 - 了解规范驱动测试流程及各步骤对应的技能。当用户问 OpenTest 是什么、OpenTest 流程、或希望按「先规划再生成」做测试但未指定具体步骤时使用。执行具体步骤请使用 opentest-new、opentest-scope、opentest-spec、opentest-generate、opentest-report、opentest-archive。
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# OpenTest 规范驱动测试(总览)
|
|
7
|
+
|
|
8
|
+
## 概述
|
|
9
|
+
|
|
10
|
+
OpenTest 是一套面向 AI 的**规范驱动测试(Spec-driven Testing)**流程:先通过结构化产物(proposal、scope、strategy、specs)与用户对齐「测什么、怎么测」,再直接生成用例资产,最后可归档。每个测试工作独立目录:`opentest/changes/<name>/`。
|
|
11
|
+
|
|
12
|
+
**理念**:先约定再生成;可与 OpenSpec 等开发侧规范对应。
|
|
13
|
+
|
|
14
|
+
## 各步骤与对应技能
|
|
15
|
+
|
|
16
|
+
执行具体步骤时,请使用下表中对应的技能(每个技能可独立加载、直接执行):
|
|
17
|
+
|
|
18
|
+
| 步骤 | 技能名 | 触发示例 | 产物/动作 |
|
|
19
|
+
|------|--------|----------|-----------|
|
|
20
|
+
| 新建 | **opentest-new** | /ottest:new 用户登录、新建测试变更 | 创建 changes/<name>/、proposal.md、specs/、artifacts/ |
|
|
21
|
+
| 范围 | **opentest-scope** | /ottest:scope、写测试范围 | scope.md |
|
|
22
|
+
| 策略与场景 | **opentest-spec** | /ottest:spec、/ottest:ff、写测试策略和场景 | strategy.md、specs/*.md |
|
|
23
|
+
| 生成 | **opentest-generate** | /ottest:generate Excel、/ottest:generate XMind | 根据 strategy+specs 直接生成 .xlsx 或 .xmind,输出到 artifacts/ |
|
|
24
|
+
| 报告 | **opentest-report** | /ottest:report、写测试报告 | report.md |
|
|
25
|
+
| 归档 | **opentest-archive** | /ottest:archive、归档测试 | 移动到 changes/archive/<date>-<name>/ |
|
|
26
|
+
|
|
27
|
+
## 目录结构
|
|
28
|
+
|
|
29
|
+
```
|
|
30
|
+
opentest/
|
|
31
|
+
changes/
|
|
32
|
+
<name>/ # 当前变更:proposal、scope、strategy、specs、artifacts、report
|
|
33
|
+
archive/ # 已归档:<date>-<name>/
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
首次使用前若无 `opentest/changes`,可在项目根运行:`.claude/skills/opentest/scripts/init_opentest.py`。
|
|
37
|
+
|
|
38
|
+
## 共享资源
|
|
39
|
+
|
|
40
|
+
- **产物模板与字段说明**:[.claude/skills/opentest/references/artifact-templates.md](.claude/skills/opentest/references/artifact-templates.md)
|
|
41
|
+
- **斜杠命令详细说明**:[.claude/skills/opentest/references/commands-reference.md](.claude/skills/opentest/references/commands-reference.md)
|
|
42
|
+
- **初始化脚本**:[.claude/skills/opentest/scripts/init_opentest.py](.claude/skills/opentest/scripts/init_opentest.py)
|
|
43
|
+
|
|
44
|
+
## 用例生成
|
|
45
|
+
|
|
46
|
+
opentest-generate 根据 strategy.md 和 specs/*.md 直接生成用例,支持 Excel(.xlsx)或 XMind(.xmind)格式,输出到当前变更的 `artifacts/`。不依赖外部技能,使用内置脚本完成生成。
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
# OpenTest 产物模板
|
|
2
|
+
|
|
3
|
+
本文档提供 proposal、scope、strategy、specs 的 Markdown 模板与字段说明,供 AI 生成或更新产物时参考。
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## proposal.md
|
|
8
|
+
|
|
9
|
+
**用途**:说明本次测试工作的背景、被测对象与目标。
|
|
10
|
+
|
|
11
|
+
```markdown
|
|
12
|
+
# 测试提案:<标题>
|
|
13
|
+
|
|
14
|
+
## 被测对象
|
|
15
|
+
- 功能/模块/版本:(简要描述)
|
|
16
|
+
- 关联需求或 OpenSpec:(如有)路径或链接
|
|
17
|
+
|
|
18
|
+
## 测试目标
|
|
19
|
+
- 为什么做这次测试(发布前验证、回归、新功能等)
|
|
20
|
+
- 期望达成的结果(如:核心流程覆盖、接口契约验证)
|
|
21
|
+
|
|
22
|
+
## 范围概要
|
|
23
|
+
- 在范围内:(一两句话)
|
|
24
|
+
- 不在范围内:(如有)
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## scope.md
|
|
30
|
+
|
|
31
|
+
**用途**:明确测试范围、边界与依赖。
|
|
32
|
+
|
|
33
|
+
```markdown
|
|
34
|
+
# 测试范围:<标题>
|
|
35
|
+
|
|
36
|
+
## 在范围内
|
|
37
|
+
- 功能/模块列表
|
|
38
|
+
- 测试类型(功能/接口/性能/兼容等)及优先级
|
|
39
|
+
|
|
40
|
+
## 不在范围内
|
|
41
|
+
- 明确排除的功能或场景
|
|
42
|
+
|
|
43
|
+
## 风险与重点
|
|
44
|
+
- 高风险区域
|
|
45
|
+
- 必须覆盖的核心场景
|
|
46
|
+
|
|
47
|
+
## 依赖
|
|
48
|
+
- 需求文档/接口文档路径
|
|
49
|
+
- 环境、数据、账号等依赖
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
## strategy.md
|
|
55
|
+
|
|
56
|
+
**用途**:测试策略与通过标准。
|
|
57
|
+
|
|
58
|
+
```markdown
|
|
59
|
+
# 测试策略:<标题>
|
|
60
|
+
|
|
61
|
+
## 测试类型
|
|
62
|
+
- 功能测试:范围与重点
|
|
63
|
+
- 接口测试:范围与重点
|
|
64
|
+
- 其他(性能/兼容/安全等):如有
|
|
65
|
+
|
|
66
|
+
## 测试层级
|
|
67
|
+
- 单元/接口/集成/E2E 等与本次相关的层级
|
|
68
|
+
|
|
69
|
+
## 通过标准
|
|
70
|
+
- 用例通过率要求
|
|
71
|
+
- 必须通过的场景或用例列表
|
|
72
|
+
- 准入/准出条件(如有)
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
## specs/<feature>.md
|
|
78
|
+
|
|
79
|
+
**用途**:按功能或需求拆分的测试场景与验收条件。
|
|
80
|
+
|
|
81
|
+
```markdown
|
|
82
|
+
# 场景:<功能/模块名>
|
|
83
|
+
|
|
84
|
+
## 场景概述
|
|
85
|
+
- 简要说明该功能要测什么
|
|
86
|
+
|
|
87
|
+
## 场景与验收条件
|
|
88
|
+
|
|
89
|
+
### 场景 1:<名称>
|
|
90
|
+
- **类型**:正向 / 负向 / 边界 / 异常
|
|
91
|
+
- **前置条件**:
|
|
92
|
+
- **步骤**:
|
|
93
|
+
- **预期结果**:
|
|
94
|
+
- **优先级**:P0 / P1 / P2
|
|
95
|
+
|
|
96
|
+
### 场景 2:…
|
|
97
|
+
(同上)
|
|
98
|
+
|
|
99
|
+
## 数据与边界
|
|
100
|
+
- 关键边界值、异常输入(如有)
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
## report.md(可选)
|
|
106
|
+
|
|
107
|
+
**用途**:测试执行结果摘要。
|
|
108
|
+
|
|
109
|
+
```markdown
|
|
110
|
+
# 测试报告:<标题>
|
|
111
|
+
|
|
112
|
+
## 执行概要
|
|
113
|
+
- 执行时间、环境
|
|
114
|
+
- 总用例数、通过/失败/阻塞
|
|
115
|
+
|
|
116
|
+
## 通过率与覆盖率
|
|
117
|
+
- 按模块或类型的通过率
|
|
118
|
+
- 需求/场景覆盖情况
|
|
119
|
+
|
|
120
|
+
## 缺陷摘要
|
|
121
|
+
- 严重/一般/轻微数量或列表(可简写)
|
|
122
|
+
|
|
123
|
+
## 结论与建议
|
|
124
|
+
- 是否达到通过标准
|
|
125
|
+
- 遗留风险或后续建议
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
---
|
|
129
|
+
|
|
130
|
+
使用说明:生成产物时不必严格照抄模板,可根据项目习惯增删章节;核心是保持「范围 → 策略 → 场景」的连贯性与可追溯性。用例生成通过 `/ottest:generate Excel` 或 `/ottest:generate XMind` 直接输出到 artifacts/。
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# OpenTest 斜杠命令参考
|
|
2
|
+
|
|
3
|
+
本文档对 `/ottest` 系列命令做详细说明与示例,便于 AI 与用户一致理解语义。
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 命令总览
|
|
8
|
+
|
|
9
|
+
| 命令 | 别名/变体 | 说明 |
|
|
10
|
+
|------|------------|------|
|
|
11
|
+
| `/ottest:new <被测对象>` | 无 | 新建一次测试工作,创建变更目录与 proposal |
|
|
12
|
+
| `/ottest:scope` | 无 | 生成或更新测试范围 scope.md |
|
|
13
|
+
| `/ottest:spec` | `/ottest:ff` | 生成策略与场景(strategy.md + specs/*.md) |
|
|
14
|
+
| `/ottest:generate Excel` 或 `XMind` | 无 | 根据 strategy+specs 直接生成用例,输出到 artifacts/ |
|
|
15
|
+
| `/ottest:report` | 无 | 生成或更新测试报告 report.md |
|
|
16
|
+
| `/ottest:archive` | 无 | 将当前变更归档到 archive/ |
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## /ottest:new \<被测对象\>
|
|
21
|
+
|
|
22
|
+
- **含义**:开始一次新的测试工作。
|
|
23
|
+
- **参数**:被测对象可以是功能名、模块名、版本号等,建议简短英文或拼音(如 `user-login`、`release-2.0`)。
|
|
24
|
+
- **行为**:
|
|
25
|
+
- 确定变更名(将空格/特殊字符替换为 `-`)。
|
|
26
|
+
- 创建 `opentest/changes/<name>/`,以及子目录 `specs/`、`artifacts/`。
|
|
27
|
+
- 若项目下尚无 `opentest/changes`,先创建。
|
|
28
|
+
- 编写 `proposal.md` 初稿,包含被测对象、测试目标、可选关联需求/OpenSpec。
|
|
29
|
+
- **示例**:
|
|
30
|
+
- 用户:「/ottest:new 用户登录」→ 创建 `opentest/changes/user-login/` 并写 proposal。
|
|
31
|
+
- 用户:「用 OpenTest 为 dark mode 建一个测试」→ 等同 `/ottest:new add-dark-mode`。
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## /ottest:scope
|
|
36
|
+
|
|
37
|
+
- **含义**:明确本次测试的范围、边界与依赖。
|
|
38
|
+
- **行为**:在**当前变更目录**下生成或更新 `scope.md`。若用户未先执行 new,需先确认或创建当前变更目录。
|
|
39
|
+
- **内容要点**:在范围 / 不在范围、风险与重点、依赖(需求、接口文档、环境)。
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## /ottest:spec 与 /ottest:ff
|
|
44
|
+
|
|
45
|
+
- **含义**:生成测试策略与场景(类似 OpenSpec 的「fast-forward」一次性产出)。
|
|
46
|
+
- **行为**:
|
|
47
|
+
- 在当前变更目录下生成 `strategy.md`(测试类型、层级、通过标准)。
|
|
48
|
+
- 在 `specs/` 下按功能或需求生成若干场景文件(如 `login.md`),每文件包含场景与验收条件(正向/负向/边界)。
|
|
49
|
+
- **输入**:可依赖 proposal、scope 以及用户补充说明。
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
## /ottest:generate [Excel|XMind]
|
|
54
|
+
|
|
55
|
+
- **含义**:根据 strategy 与 specs 直接生成测试用例。
|
|
56
|
+
- **参数**:`Excel` 生成 .xlsx,`XMind` 生成 .xmind;可省略则默认 Excel;可写 `Excel,XMind` 同时生成两种格式。
|
|
57
|
+
- **行为**:
|
|
58
|
+
- 读取当前变更的 `strategy.md`、`specs/*.md`。
|
|
59
|
+
- 从 specs 中解析场景与验收条件,转换为结构化用例。
|
|
60
|
+
- 调用 `opentest-generate` 内置脚本生成 .xlsx 或 .xmind 文件。
|
|
61
|
+
- 将生成文件放入当前变更的 `artifacts/`。
|
|
62
|
+
- 在回复中列出生成的文件路径与简要说明。
|
|
63
|
+
- **示例**:`/ottest:generate Excel`、`/ottest:generate XMind`、`/ottest:generate Excel,XMind`
|
|
64
|
+
|
|
65
|
+
---
|
|
66
|
+
|
|
67
|
+
## /ottest:report
|
|
68
|
+
|
|
69
|
+
- **含义**:编写或更新测试执行结果摘要。
|
|
70
|
+
- **行为**:在当前变更目录下生成或更新 `report.md`,内容可包括执行概要、通过率、覆盖率、缺陷摘要、结论与建议。具体字段可按项目习惯调整。
|
|
71
|
+
|
|
72
|
+
---
|
|
73
|
+
|
|
74
|
+
## /ottest:archive
|
|
75
|
+
|
|
76
|
+
- **含义**:结束本次测试工作并归档。
|
|
77
|
+
- **行为**:将当前变更目录整体移动到 `opentest/changes/archive/<date>-<name>/`(日期建议 YYYY-MM-DD),并告知用户归档路径。归档后该变更视为已完成。
|
|
78
|
+
|
|
79
|
+
---
|
|
80
|
+
|
|
81
|
+
## 「当前变更」的确定
|
|
82
|
+
|
|
83
|
+
- 若用户明确说「对 xxx 做 scope / spec / …」,则当前变更为 `opentest/changes/xxx/`。
|
|
84
|
+
- 若仅有一个未归档的变更目录,可默认其为当前变更。
|
|
85
|
+
- 若有多个未归档变更,应询问用户要对哪一个执行命令,或让用户用「对 \<name\> 做 /ottest:scope」等形式指定。
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
OpenTest 初始化脚本:在项目根目录创建 opentest 目录结构。
|
|
4
|
+
|
|
5
|
+
用法(在项目根目录执行):
|
|
6
|
+
python scripts/init_opentest.py
|
|
7
|
+
或指定项目根:
|
|
8
|
+
python scripts/init_opentest.py --root /path/to/project
|
|
9
|
+
|
|
10
|
+
创建结构:
|
|
11
|
+
opentest/
|
|
12
|
+
changes/ # 存放各次测试变更
|
|
13
|
+
archive/ # 归档目录(可选,首次 new 时也可创建)
|
|
14
|
+
"""
|
|
15
|
+
import argparse
|
|
16
|
+
import os
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def main():
|
|
20
|
+
parser = argparse.ArgumentParser(description="Initialize OpenTest directory structure")
|
|
21
|
+
parser.add_argument(
|
|
22
|
+
"--root",
|
|
23
|
+
default=os.getcwd(),
|
|
24
|
+
help="Project root directory (default: current working directory)",
|
|
25
|
+
)
|
|
26
|
+
args = parser.parse_args()
|
|
27
|
+
root = os.path.abspath(args.root)
|
|
28
|
+
|
|
29
|
+
opentest = os.path.join(root, "opentest")
|
|
30
|
+
changes = os.path.join(opentest, "changes")
|
|
31
|
+
archive = os.path.join(opentest, "archive")
|
|
32
|
+
|
|
33
|
+
created = []
|
|
34
|
+
for d in (opentest, changes, archive):
|
|
35
|
+
if not os.path.isdir(d):
|
|
36
|
+
os.makedirs(d, exist_ok=True)
|
|
37
|
+
created.append(d)
|
|
38
|
+
|
|
39
|
+
if created:
|
|
40
|
+
print("Created:", ", ".join(created))
|
|
41
|
+
else:
|
|
42
|
+
print("OpenTest directories already exist under:", opentest)
|
|
43
|
+
print("You can now use /ottest:new <name> to create a new test change.")
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
if __name__ == "__main__":
|
|
47
|
+
main()
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: opentest-apply
|
|
3
|
+
description: [已废弃] OpenTest 曾用步骤,已由 opentest-generate 替代。请使用 /ottest:generate Excel 或 /ottest:generate XMind 根据 strategy+specs 直接生成用例。
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# opentest-apply:已废弃
|
|
7
|
+
|
|
8
|
+
本技能已废弃,由 **opentest-generate** 替代。OpenTest 流程已简化为:
|
|
9
|
+
|
|
10
|
+
```
|
|
11
|
+
new → scope → spec → generate → report → archive
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
如需生成测试用例,请使用 **opentest-generate**:
|
|
15
|
+
|
|
16
|
+
- `/ottest:generate Excel` — 根据 strategy.md 和 specs/*.md 生成 .xlsx 用例
|
|
17
|
+
- `/ottest:generate XMind` — 生成 .xmind 思维导图
|
|
18
|
+
- `/ottest:generate Excel,XMind` — 同时生成两种格式
|
|
19
|
+
|
|
20
|
+
opentest-generate 不依赖外部技能,使用内置脚本完成生成,输出到当前变更的 `artifacts/`。
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: opentest-archive
|
|
3
|
+
description: OpenTest 归档 - 将当前测试变更移动到 archive 目录。当用户要归档测试、结束本次测试工作、或执行 /ottest:archive 时使用。将 opentest/changes/<name>/ 移动到 opentest/changes/archive/<date>-<name>/。
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# opentest-archive:归档测试变更
|
|
7
|
+
|
|
8
|
+
## 职责
|
|
9
|
+
|
|
10
|
+
将当前 OpenTest 变更目录整体移动到 `opentest/changes/archive/<date>-<name>/`,完成本次测试工作的收尾;归档名建议带日期(如 `2025-02-11-add-dark-mode`)。
|
|
11
|
+
|
|
12
|
+
## 当前变更目录
|
|
13
|
+
|
|
14
|
+
- 若用户指定了变更名,使用 `opentest/changes/<name>/`。
|
|
15
|
+
- 若未指定且项目下仅有一个非归档变更目录,则默认使用该目录。
|
|
16
|
+
- 若有多个未归档变更,询问用户要归档哪一个。
|
|
17
|
+
|
|
18
|
+
## 执行步骤
|
|
19
|
+
|
|
20
|
+
1. **确定当前变更目录**(按上规则),记为 `<name>`。
|
|
21
|
+
2. **确定归档名**:`<date>-<name>`,其中日期建议为 YYYY-MM-DD(可用当前日期)。
|
|
22
|
+
3. **移动目录**:将 `opentest/changes/<name>/` 整体移动到 `opentest/changes/archive/<date>-<name>/`。若 `opentest/changes/archive/` 不存在,先创建。
|
|
23
|
+
4. **告知用户**:归档后的路径 `opentest/changes/archive/<date>-<name>/`,并说明该变更已结束,可开始新的测试工作(使用 opentest-new)。
|
|
24
|
+
|
|
25
|
+
## 产物
|
|
26
|
+
|
|
27
|
+
- 原 `opentest/changes/<name>/` 不再存在;其内容现位于 `opentest/changes/archive/<date>-<name>/`。
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: opentest-design
|
|
3
|
+
description: [已废弃] OpenTest 曾用步骤,已由简化流程替代。请使用 opentest-generate(/ottest:generate Excel 或 XMind)在 spec 后直接生成用例。
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# opentest-design:已废弃
|
|
7
|
+
|
|
8
|
+
本技能已废弃。OpenTest 流程已简化为:
|
|
9
|
+
|
|
10
|
+
```
|
|
11
|
+
new → scope → spec → generate → report → archive
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
设计步骤(design)已合并到生成步骤。如需生成测试用例,请使用 **opentest-generate**:
|
|
15
|
+
|
|
16
|
+
- `/ottest:generate Excel` — 生成 .xlsx 用例
|
|
17
|
+
- `/ottest:generate XMind` — 生成 .xmind 思维导图
|
|
18
|
+
- `/ottest:generate Excel,XMind` — 同时生成两种格式
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: opentest-generate
|
|
3
|
+
description: OpenTest 用例生成 - 根据 strategy.md 和 specs/*.md 直接生成 Excel 或 XMind 格式的测试用例。当用户要生成用例、执行 /ottest:generate Excel 或 /ottest:generate XMind 时使用。不依赖外部技能,使用内置脚本完成生成。
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# opentest-generate:用例生成
|
|
7
|
+
|
|
8
|
+
## 职责
|
|
9
|
+
|
|
10
|
+
读取当前变更的 `strategy.md` 和 `specs/*.md`,根据用户指定的格式(Excel 或 XMind)直接生成测试用例文件,输出到当前变更的 `artifacts/` 目录。不依赖 excel-test-case-generator、xmind-test-case-generator 等外部技能,使用本技能内置脚本完成生成。
|
|
11
|
+
|
|
12
|
+
## 当前变更目录
|
|
13
|
+
|
|
14
|
+
- 若用户指定了变更名,使用 `opentest/changes/<name>/`。
|
|
15
|
+
- 若未指定且项目下仅有一个非归档变更目录,则默认使用该目录。
|
|
16
|
+
- 若有多个未归档变更,询问用户要对哪一个执行。
|
|
17
|
+
|
|
18
|
+
## 输出格式
|
|
19
|
+
|
|
20
|
+
- **Excel**:生成 .xlsx 文件,包含用例编号、用例名称、前置条件、测试步骤、预期结果、优先级等标准字段。
|
|
21
|
+
- **XMind**:生成 .xmind 思维导图,按功能模块组织,包含正向用例、负向用例、边界值用例、异常用例等分类。
|
|
22
|
+
|
|
23
|
+
可同时生成两种格式,如 `/ottest:generate Excel,XMind`。
|
|
24
|
+
|
|
25
|
+
## 执行步骤
|
|
26
|
+
|
|
27
|
+
1. **确定当前变更目录**(按上规则)。
|
|
28
|
+
2. **读取上下文**:读取 `strategy.md`、`specs/*.md`(以及可选的 scope.md、proposal.md)。
|
|
29
|
+
3. **从 specs 提取测试用例**:解析每个 spec 文件中的「场景与验收条件」,将其转换为结构化测试用例列表。每个用例包含:
|
|
30
|
+
- 功能/模块(来自 spec 文件名或场景概述)
|
|
31
|
+
- 用例名称
|
|
32
|
+
- 类型:正向 / 负向 / 边界 / 异常
|
|
33
|
+
- 前置条件、步骤、预期结果
|
|
34
|
+
- 优先级:P0 / P1 / P2
|
|
35
|
+
4. **生成 testcases.json**:在变更目录或 artifacts/ 下写入临时 `testcases.json`,供脚本读取。格式示例:
|
|
36
|
+
|
|
37
|
+
```json
|
|
38
|
+
[
|
|
39
|
+
{
|
|
40
|
+
"feature": "登录",
|
|
41
|
+
"name": "正常登录-有效账号密码",
|
|
42
|
+
"type": "正向",
|
|
43
|
+
"preconditions": "系统已启动,用户已注册",
|
|
44
|
+
"steps": "1. 打开登录页\n2. 输入正确账号密码\n3. 点击登录",
|
|
45
|
+
"expected_result": "登录成功,跳转首页",
|
|
46
|
+
"priority": "P0"
|
|
47
|
+
}
|
|
48
|
+
]
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
5. **调用生成脚本**:
|
|
52
|
+
- **Excel**:执行
|
|
53
|
+
```powershell
|
|
54
|
+
python .claude/skills/opentest-generate/scripts/generate_excel.py --input <变更目录>/testcases.json --output <变更目录>/artifacts/<name>_cases.xlsx
|
|
55
|
+
```
|
|
56
|
+
- **XMind**:执行
|
|
57
|
+
```powershell
|
|
58
|
+
python .claude/skills/opentest-generate/scripts/generate_xmind.py --input <变更目录>/testcases.json --output <变更目录>/artifacts/<name>_cases.xmind --title "测试用例"
|
|
59
|
+
```
|
|
60
|
+
6. **清理**:可删除临时 testcases.json,或保留供用户审查。
|
|
61
|
+
7. **告知用户**:列出生成的文件路径及简要说明。
|
|
62
|
+
|
|
63
|
+
## 依赖
|
|
64
|
+
|
|
65
|
+
- **Excel**:需要 `openpyxl`(`pip install openpyxl`)
|
|
66
|
+
- **XMind**:无额外依赖,使用内置 ZIP + content.json 格式生成,兼容 XMind Zen
|
|
67
|
+
|
|
68
|
+
若 Excel 生成失败且提示缺少 openpyxl,请运行 `pip install openpyxl`。
|
|
69
|
+
|
|
70
|
+
## 产物
|
|
71
|
+
|
|
72
|
+
- `opentest/changes/<name>/artifacts/<name>_cases.xlsx`(Excel 格式)
|
|
73
|
+
- `opentest/changes/<name>/artifacts/<name>_cases.xmind`(XMind 格式)
|
|
74
|
+
|
|
75
|
+
文件名可根据变更名或用户指定调整。
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
OpenTest Excel 用例生成脚本:根据 testcases.json 生成 .xlsx 测试用例文档。
|
|
4
|
+
|
|
5
|
+
用法:
|
|
6
|
+
python generate_excel.py --input testcases.json --output artifacts/cases.xlsx
|
|
7
|
+
"""
|
|
8
|
+
import argparse
|
|
9
|
+
import json
|
|
10
|
+
import os
|
|
11
|
+
import sys
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def create_excel_with_openpyxl(test_cases: list, output_path: str) -> None:
|
|
15
|
+
"""使用 openpyxl 创建 Excel 文件。"""
|
|
16
|
+
try:
|
|
17
|
+
from openpyxl import Workbook
|
|
18
|
+
from openpyxl.styles import Font, PatternFill, Alignment, Border, Side
|
|
19
|
+
from openpyxl.utils import get_column_letter
|
|
20
|
+
except ImportError:
|
|
21
|
+
print("错误:需要安装 openpyxl,请运行:pip install openpyxl", file=sys.stderr)
|
|
22
|
+
sys.exit(1)
|
|
23
|
+
|
|
24
|
+
wb = Workbook()
|
|
25
|
+
sheet = wb.active
|
|
26
|
+
sheet.title = "测试用例"
|
|
27
|
+
|
|
28
|
+
headers = ["用例编号", "用例名称", "功能模块", "类型", "前置条件", "测试步骤", "预期结果", "优先级", "执行结果"]
|
|
29
|
+
header_fill = PatternFill(start_color="4472C4", end_color="4472C4", fill_type="solid")
|
|
30
|
+
header_font = Font(bold=True, color="FFFFFF", size=11)
|
|
31
|
+
|
|
32
|
+
for col_idx, header in enumerate(headers, 1):
|
|
33
|
+
cell = sheet.cell(row=1, column=col_idx)
|
|
34
|
+
cell.value = header
|
|
35
|
+
cell.font = header_font
|
|
36
|
+
cell.fill = header_fill
|
|
37
|
+
cell.alignment = Alignment(horizontal="center", vertical="center")
|
|
38
|
+
|
|
39
|
+
column_widths = [12, 35, 12, 10, 25, 40, 30, 10, 12]
|
|
40
|
+
for col_idx, width in enumerate(column_widths, 1):
|
|
41
|
+
sheet.column_dimensions[get_column_letter(col_idx)].width = width
|
|
42
|
+
|
|
43
|
+
for row_idx, tc in enumerate(test_cases, 2):
|
|
44
|
+
tc_id = f"TC-{row_idx - 1:03d}"
|
|
45
|
+
name = tc.get("name", "")
|
|
46
|
+
feature = tc.get("feature", "")
|
|
47
|
+
tc_type = tc.get("type", "")
|
|
48
|
+
preconditions = tc.get("preconditions", "")
|
|
49
|
+
steps = tc.get("steps", "")
|
|
50
|
+
expected = tc.get("expected_result", tc.get("expected", ""))
|
|
51
|
+
priority = tc.get("priority", "P1")
|
|
52
|
+
sheet.cell(row=row_idx, column=1, value=tc_id)
|
|
53
|
+
sheet.cell(row=row_idx, column=2, value=name)
|
|
54
|
+
sheet.cell(row=row_idx, column=3, value=feature)
|
|
55
|
+
sheet.cell(row=row_idx, column=4, value=tc_type)
|
|
56
|
+
sheet.cell(row=row_idx, column=5, value=preconditions)
|
|
57
|
+
sheet.cell(row=row_idx, column=6, value=steps)
|
|
58
|
+
sheet.cell(row=row_idx, column=7, value=expected)
|
|
59
|
+
sheet.cell(row=row_idx, column=8, value=priority)
|
|
60
|
+
sheet.cell(row=row_idx, column=9, value="")
|
|
61
|
+
|
|
62
|
+
os.makedirs(os.path.dirname(output_path) or ".", exist_ok=True)
|
|
63
|
+
wb.save(output_path)
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def main():
|
|
67
|
+
parser = argparse.ArgumentParser(description="Generate Excel test cases from JSON")
|
|
68
|
+
parser.add_argument("--input", "-i", required=True, help="Path to testcases.json")
|
|
69
|
+
parser.add_argument("--output", "-o", required=True, help="Output .xlsx path")
|
|
70
|
+
args = parser.parse_args()
|
|
71
|
+
|
|
72
|
+
with open(args.input, encoding="utf-8-sig") as f:
|
|
73
|
+
test_cases = json.load(f)
|
|
74
|
+
|
|
75
|
+
if not isinstance(test_cases, list):
|
|
76
|
+
print("错误:JSON 应为用例数组", file=sys.stderr)
|
|
77
|
+
sys.exit(1)
|
|
78
|
+
|
|
79
|
+
create_excel_with_openpyxl(test_cases, args.output)
|
|
80
|
+
print(f"已生成:{args.output}")
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
if __name__ == "__main__":
|
|
84
|
+
main()
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
OpenTest XMind 用例生成脚本:根据 testcases.json 生成 .xmind 测试用例思维导图。
|
|
4
|
+
|
|
5
|
+
用法:
|
|
6
|
+
python generate_xmind.py --input testcases.json --output artifacts/cases.xmind --title "测试用例"
|
|
7
|
+
"""
|
|
8
|
+
import argparse
|
|
9
|
+
import json
|
|
10
|
+
import os
|
|
11
|
+
import sys
|
|
12
|
+
from collections import defaultdict
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def build_xmind_structure(test_cases: list, root_title: str) -> list:
|
|
16
|
+
"""将扁平用例列表组织为 XMind 层级结构。"""
|
|
17
|
+
by_feature = defaultdict(lambda: defaultdict(list))
|
|
18
|
+
for tc in test_cases:
|
|
19
|
+
feature = tc.get("feature", "未分类")
|
|
20
|
+
tc_type = tc.get("type", "其他")
|
|
21
|
+
name = tc.get("name", "")
|
|
22
|
+
by_feature[feature][tc_type].append(name)
|
|
23
|
+
|
|
24
|
+
type_order = ["正向", "负向", "边界", "异常", "其他"]
|
|
25
|
+
root = {"title": root_title, "children": []}
|
|
26
|
+
for feature in sorted(by_feature.keys()):
|
|
27
|
+
feat_node = {"title": f"{feature} - 测试用例", "children": []}
|
|
28
|
+
for tc_type in type_order:
|
|
29
|
+
cases = by_feature[feature].get(tc_type)
|
|
30
|
+
if not cases:
|
|
31
|
+
continue
|
|
32
|
+
type_node = {"title": f"{tc_type}用例", "children": []}
|
|
33
|
+
for name in cases:
|
|
34
|
+
type_node["children"].append({"title": name, "children": []})
|
|
35
|
+
feat_node["children"].append(type_node)
|
|
36
|
+
for tc_type, cases in by_feature[feature].items():
|
|
37
|
+
if tc_type not in type_order:
|
|
38
|
+
type_node = {"title": f"{tc_type}用例", "children": []}
|
|
39
|
+
for name in cases:
|
|
40
|
+
type_node["children"].append({"title": name, "children": []})
|
|
41
|
+
feat_node["children"].append(type_node)
|
|
42
|
+
root["children"].append(feat_node)
|
|
43
|
+
|
|
44
|
+
return [root]
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def create_xmind_manual(structure: list, output_path: str) -> None:
|
|
48
|
+
"""手动创建 XMind Zen 格式(ZIP + content.json)。"""
|
|
49
|
+
import zipfile
|
|
50
|
+
|
|
51
|
+
def to_xmind_json(nodes: list) -> list:
|
|
52
|
+
result = []
|
|
53
|
+
for i, node in enumerate(nodes):
|
|
54
|
+
child = {
|
|
55
|
+
"id": f"topic-{id(node)}",
|
|
56
|
+
"title": node.get("title", ""),
|
|
57
|
+
"children": {"attached": to_xmind_json(node.get("children", []))},
|
|
58
|
+
}
|
|
59
|
+
result.append(child)
|
|
60
|
+
return result
|
|
61
|
+
|
|
62
|
+
root = structure[0] if structure else {}
|
|
63
|
+
content = {
|
|
64
|
+
"root": {
|
|
65
|
+
"id": "root",
|
|
66
|
+
"title": root.get("title", "测试用例"),
|
|
67
|
+
"children": {"attached": to_xmind_json(root.get("children", []))},
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
os.makedirs(os.path.dirname(output_path) or ".", exist_ok=True)
|
|
72
|
+
with zipfile.ZipFile(output_path, "w", zipfile.ZIP_DEFLATED) as zf:
|
|
73
|
+
zf.writestr("content.json", json.dumps(content, ensure_ascii=False, indent=2))
|
|
74
|
+
zf.writestr(
|
|
75
|
+
"metadata.json",
|
|
76
|
+
json.dumps({"creator": {"name": "OpenTest", "version": "1.0"}}, ensure_ascii=False),
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def main():
|
|
81
|
+
parser = argparse.ArgumentParser(description="Generate XMind test cases from JSON")
|
|
82
|
+
parser.add_argument("--input", "-i", required=True, help="Path to testcases.json")
|
|
83
|
+
parser.add_argument("--output", "-o", required=True, help="Output .xmind path")
|
|
84
|
+
parser.add_argument("--title", "-t", default="测试用例", help="Root topic title")
|
|
85
|
+
args = parser.parse_args()
|
|
86
|
+
|
|
87
|
+
with open(args.input, encoding="utf-8-sig") as f:
|
|
88
|
+
test_cases = json.load(f)
|
|
89
|
+
|
|
90
|
+
if not isinstance(test_cases, list):
|
|
91
|
+
print("错误:JSON 应为用例数组", file=sys.stderr)
|
|
92
|
+
sys.exit(1)
|
|
93
|
+
|
|
94
|
+
structure = build_xmind_structure(test_cases, args.title)
|
|
95
|
+
create_xmind_manual(structure, args.output)
|
|
96
|
+
|
|
97
|
+
print(f"已生成:{args.output}")
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
if __name__ == "__main__":
|
|
101
|
+
main()
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: opentest-new
|
|
3
|
+
description: OpenTest 新建测试工作 - 创建一次测试变更目录并编写 proposal。当用户要新建 OpenTest 测试工作、创建测试变更、或执行 /ottest:new 时使用。创建 opentest/changes/<name>/ 及 proposal.md、specs/、artifacts/。
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# opentest-new:新建测试工作
|
|
7
|
+
|
|
8
|
+
## 职责
|
|
9
|
+
|
|
10
|
+
新建一次 OpenTest 测试工作:创建变更目录并编写测试提案初稿。
|
|
11
|
+
|
|
12
|
+
## 确定变更名
|
|
13
|
+
|
|
14
|
+
- 从用户输入中提取被测对象(功能名、模块名、版本号等)。
|
|
15
|
+
- 将名称规范为短名:英文或拼音,空格与特殊字符替换为 `-`(如「用户登录」→ `user-login`,`release 2.0` → `release-2.0`)。
|
|
16
|
+
|
|
17
|
+
## 执行步骤
|
|
18
|
+
|
|
19
|
+
1. **确保根目录存在**:若项目下没有 `opentest/changes`,先创建(或运行 `.claude/skills/opentest/scripts/init_opentest.py`,工作目录为项目根)。
|
|
20
|
+
2. **创建变更目录**:`opentest/changes/<name>/`,以及子目录 `specs/`、`artifacts/`。
|
|
21
|
+
3. **编写 proposal.md**:在变更目录下创建 `proposal.md`,包含:
|
|
22
|
+
- 被测对象(功能/模块/版本)
|
|
23
|
+
- 测试目标与原因
|
|
24
|
+
- 可选:关联的 OpenSpec 或需求路径
|
|
25
|
+
- 模板与字段说明见:`.claude/skills/opentest/references/artifact-templates.md` 中的「proposal.md」小节。
|
|
26
|
+
|
|
27
|
+
4. **告知用户**:变更目录路径及下一步可执行「写测试范围」或 `/ottest:scope`。
|
|
28
|
+
|
|
29
|
+
## 产物
|
|
30
|
+
|
|
31
|
+
- `opentest/changes/<name>/proposal.md`
|
|
32
|
+
- `opentest/changes/<name>/specs/`(空目录)
|
|
33
|
+
- `opentest/changes/<name>/artifacts/`(空目录)
|