@wwkit/harness 1.0.9 → 1.0.10
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/agents/extract.md +2 -14
- package/agents/lint.md +367 -0
- package/agents/pyit.md +361 -0
- package/agents/pyut.md +347 -0
- package/agents/query.md +2 -14
- package/agents/revise.md +2 -14
- package/agents/work.md +151 -0
- package/commands/git-sync.md +218 -0
- package/commands/net-port.md +364 -0
- package/commands/pyit.md +6 -0
- package/commands/pyut.md +6 -0
- package/commands/resume.md +104 -0
- package/package.json +2 -2
- package/skills/better-skill/SKILL.md +124 -0
- package/skills/blame-skill/SKILL.md +201 -0
- package/skills/lint-ai-fix/SKILL.md +141 -0
- package/skills/lint-config-setup/SKILL.md +106 -0
- package/skills/lint-config-setup/references/languages/js.md +110 -0
- package/skills/lint-config-setup/references/languages/py.md +89 -0
- package/skills/lint-env-ensure/SKILL.md +92 -0
- package/skills/lint-env-ensure/references/config.md +65 -0
- package/skills/lint-language-detect/SKILL.md +79 -0
- package/skills/lint-language-detect/references/detect-language.js +65 -0
- package/skills/lint-rules-analyze/SKILL.md +129 -0
- package/skills/lint-suitability-check/SKILL.md +84 -0
- package/skills/lint-tool-fix/SKILL.md +94 -0
- package/skills/new-skill/SKILL.md +227 -0
- package/skills/new-skill/references/template.md +53 -0
- package/skills/new-skill/references/workflow-patterns.md +104 -0
- package/skills/pytest-case-create/SKILL.md +327 -0
- package/skills/pytest-case-create/references/test-standards.md +244 -0
- package/skills/pytest-case-fix/SKILL.md +274 -0
- package/skills/pytest-coverage-analyze/SKILL.md +226 -0
- package/skills/pytest-coverage-analyze/references/scoring-rules.md +57 -0
- package/skills/pytest-env-ensure/SKILL.md +198 -0
- package/skills/pytest-env-ensure/references/config.md +145 -0
- package/skills/pytest-execute/SKILL.md +155 -0
- package/skills/pytest-sample/SKILL.md +164 -0
- package/skills/pytest-sample/references/src/pytest-sample/Calculator.py +67 -0
- package/skills/pytest-sample/references/src/pytest-sample/ConfigManager.py +68 -0
- package/skills/pytest-sample/references/src/pytest-sample/FileProcessor.py +53 -0
- package/skills/pytest-sample/references/src/pytest-sample/OrderService.py +82 -0
- package/skills/pytest-sample/references/src/pytest-sample/TokenGenerator.py +50 -0
- package/skills/pytest-sample/references/src/pytest-sample/UserService.py +45 -0
- package/skills/pytest-sample/references/src/pytest-sample/__init__.py +0 -0
- package/skills/pytest-suitability-check/SKILL.md +224 -0
- package/skills/read-docs/SKILL.md +134 -0
- package/skills/read-docs/references/opencode/agents/cases.md +206 -0
- package/skills/read-docs/references/opencode/agents/design-pattern.md +47 -0
- package/skills/read-docs/references/opencode/agents/detail.md +191 -0
- package/skills/read-docs/references/opencode/agents/examples.md +100 -0
- package/skills/read-docs/references/opencode/agents/index.md +307 -0
- package/skills/read-docs/references/opencode/agents/workflow.md +161 -0
- package/skills/read-docs/references/opencode/cli/commands/acp.md +32 -0
- package/skills/read-docs/references/opencode/cli/commands/agent.md +16 -0
- package/skills/read-docs/references/opencode/cli/commands/attach.md +20 -0
- package/skills/read-docs/references/opencode/cli/commands/mcp.md +37 -0
- package/skills/read-docs/references/opencode/cli/commands/others.md +49 -0
- package/skills/read-docs/references/opencode/cli/commands/plugin.md +13 -0
- package/skills/read-docs/references/opencode/cli/commands/provider.md +44 -0
- package/skills/read-docs/references/opencode/cli/commands/run.md +81 -0
- package/skills/read-docs/references/opencode/cli/commands/serve.md +84 -0
- package/skills/read-docs/references/opencode/cli/commands/session.md +38 -0
- package/skills/read-docs/references/opencode/cli/commands/web.md +15 -0
- package/skills/read-docs/references/opencode/cli/env.md +39 -0
- package/skills/read-docs/references/opencode/cli/index.md +19 -0
- package/skills/read-docs/references/opencode/cli/tui.md +35 -0
- package/skills/read-docs/references/opencode/commands/examples.md +42 -0
- package/skills/read-docs/references/opencode/commands/index.md +185 -0
- package/skills/read-docs/references/opencode/config/provider.md +152 -0
- package/skills/read-docs/references/opencode/formatter/index.md +71 -0
- package/skills/read-docs/references/opencode/guide/config.md +419 -0
- package/skills/read-docs/references/opencode/guide/formatters.md +70 -0
- package/skills/read-docs/references/opencode/guide/index.md +37 -0
- package/skills/read-docs/references/opencode/guide/providers.md +31 -0
- package/skills/read-docs/references/opencode/guide/rules.md +63 -0
- package/skills/read-docs/references/opencode/plugins/examples.md +75 -0
- package/skills/read-docs/references/opencode/plugins/index.md +188 -0
- package/skills/read-docs/references/opencode/reference/index.md +119 -0
- package/skills/read-docs/references/opencode/rule/index.md +78 -0
- package/skills/read-docs/references/opencode/skills/detail.md +113 -0
- package/skills/read-docs/references/opencode/skills/examples.md +141 -0
- package/skills/read-docs/references/opencode/skills/index.md +126 -0
- package/skills/read-docs/references/opencode/skills/workflow.md +146 -0
- package/skills/read-docs/references/opencode/tests/agent.md +10 -0
- package/skills/read-docs/references/opencode/tests/config.md +60 -0
- package/skills/read-docs/references/opencode/tests/file.md +12 -0
- package/skills/read-docs/references/opencode/tests/serve.md +18 -0
- package/skills/read-docs/references/opencode/tests/session.md +31 -0
- package/skills/read-docs/references/opencode/tests/web.md +17 -0
- package/skills/read-docs/references/opencode/tools/arguments.md +305 -0
- package/skills/read-docs/references/opencode/tools/context.md +18 -0
- package/skills/read-docs/references/opencode/tools/custom.md +111 -0
- package/skills/read-docs/references/opencode/tools/detail.md +104 -0
- package/skills/read-docs/references/opencode/tools/examples.md +71 -0
- package/skills/read-docs/references/opencode/tools/index.md +56 -0
- package/skills/read-docs/references/opencode/tools/lsp.md +26 -0
- package/skills/read-docs/references/opencode/tools/mcp.md +132 -0
- package/skills/read-docs/references/opencode/train/README.md +135 -0
- package/skills/read-docs/references/opencode/train/agent-basic.md +772 -0
- package/skills/read-docs/references/opencode/train/command-basic.md +668 -0
- package/skills/read-docs/references/opencode/train/config-basic.md +509 -0
- package/skills/read-docs/references/opencode/train/index.md +164 -0
- package/skills/read-docs/references/opencode/train/practice.md +873 -0
- package/skills/read-docs/references/opencode/train/skill-basic.md +608 -0
- package/skills/read-docs/references/opencode/tui/commands/config.md +32 -0
- package/skills/read-docs/references/opencode/tui/commands/editor.md +47 -0
- package/skills/read-docs/references/opencode/tui/commands/index.md +125 -0
- package/skills/read-docs/references/opencode/tui/commands/init.md +5 -0
- package/skills/read-docs/references/opencode/tui/index.md +26 -0
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: pytest-case-create
|
|
3
|
+
description: |
|
|
4
|
+
按测试规范创建 pytest 测试用例。根据 test_type 切换 Mock 策略,分析源码识别未覆盖区域,按 AAA/FIRST/边界值规范创建用例。
|
|
5
|
+
适用:测试代理外层 loop 的用例创建阶段,为未覆盖代码创建测试用例。
|
|
6
|
+
不适用:修复已有失败用例(使用 pytest-case-fix)、非 pytest 框架的用例创建。
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# 测试用例创建
|
|
10
|
+
|
|
11
|
+
## 工作流模式
|
|
12
|
+
|
|
13
|
+
本 Skill 采用 **顺序执行** 工作流:
|
|
14
|
+
|
|
15
|
+
1. **扫描已有测试**:检查目标模块是否已有测试文件,提取已有方法名
|
|
16
|
+
2. **加载规范**:读取用例创建规范
|
|
17
|
+
3. **分析源码**:识别未覆盖的函数、分支、异常路径
|
|
18
|
+
4. **决策 Mock 策略**:按 test_type 选择 Mock 策略
|
|
19
|
+
5. **创建用例**:增量追加或全新创建
|
|
20
|
+
6. **语法验证**:验证创建的测试文件语法正确
|
|
21
|
+
|
|
22
|
+
## 输入
|
|
23
|
+
|
|
24
|
+
| 参数 | 必填 | 类型 | 说明 |
|
|
25
|
+
|------|------|------|------|
|
|
26
|
+
| uncovered_areas | 是 | list/string | 未覆盖区域清单(来自 pytest-coverage-analyze,首轮为 "all") |
|
|
27
|
+
| testable_classes | 否 | list | 可测试类清单(来自 pytest-suitability-check,含文件路径+类名+分类) |
|
|
28
|
+
| target | 是 | string | 被测模块路径 |
|
|
29
|
+
| test_dir | 是 | string | 测试目录路径(由 agent Step 1 统一解析) |
|
|
30
|
+
| test_type | 是 | string | 测试类型:unit 或 integration |
|
|
31
|
+
| config | 是 | object | 来自 pytest-env-ensure 的配置对象 |
|
|
32
|
+
|
|
33
|
+
## 输出
|
|
34
|
+
|
|
35
|
+
- 新增/追加的测试文件列表(标注是全新创建还是增量追加)
|
|
36
|
+
- 每个文件新增的用例数
|
|
37
|
+
- 跳过的重复用例数
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## 阶段零:扫描已有测试
|
|
42
|
+
|
|
43
|
+
**操作**:使用 agent 传入的 `test_dir` 直接扫描已有测试,确定工作模式
|
|
44
|
+
|
|
45
|
+
1. 直接使用传入的 `test_dir` 路径(不再自行推导目录映射)
|
|
46
|
+
|
|
47
|
+
2. 扫描该目录下是否已有 `test_*.py` 文件
|
|
48
|
+
|
|
49
|
+
3. **无已有文件** → 标记为 `全新模式`,直接进入阶段一
|
|
50
|
+
|
|
51
|
+
4. **有已有文件** → 标记为 `增量模式`,执行以下操作:
|
|
52
|
+
- 逐个读取已有测试文件
|
|
53
|
+
- 提取已有测试方法名集合(`def test_xxx` 的完整方法名)
|
|
54
|
+
- 提取已有 import 语句和 fixture 定义
|
|
55
|
+
- 记录已有测试类名
|
|
56
|
+
|
|
57
|
+
**决策**:
|
|
58
|
+
|
|
59
|
+
| 模式 | 后续行为 |
|
|
60
|
+
|------|---------|
|
|
61
|
+
| 全新模式 | 创建新文件,从头编写用例 |
|
|
62
|
+
| 增量模式 | 在已有文件中追加方法,跳过已存在的方法名 |
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
## 阶段一:加载规范
|
|
67
|
+
|
|
68
|
+
**操作**:读取 `references/test-standards.md`,加载:
|
|
69
|
+
- 通用规范(AAA、FIRST、命名)
|
|
70
|
+
- Python/pytest 特定规范(fixture、mock、parametrize)
|
|
71
|
+
- test_type 对应的专属规范
|
|
72
|
+
|
|
73
|
+
---
|
|
74
|
+
|
|
75
|
+
## 阶段二:分析源码
|
|
76
|
+
|
|
77
|
+
**输入**:uncovered_areas 清单 + testable_classes 清单(如有)
|
|
78
|
+
|
|
79
|
+
**操作**:
|
|
80
|
+
|
|
81
|
+
1. 确定待分析源码范围:
|
|
82
|
+
- 如果 `uncovered_areas == "all"`(首轮):扫描 target 目录下所有 .py 文件
|
|
83
|
+
- 如果 `uncovered_areas` 为列表:仅读取列表中的未覆盖文件
|
|
84
|
+
- 如果提供了 `testable_classes`:仅分析清单中列出的类,跳过不在清单中的类
|
|
85
|
+
|
|
86
|
+
2. 逐个读取待分析的源码文件
|
|
87
|
+
|
|
88
|
+
3. 分析每个未覆盖函数:
|
|
89
|
+
- 函数签名(参数、返回值)
|
|
90
|
+
- 内部分支(if/elif/else)
|
|
91
|
+
- 异常处理(raise/try-except)
|
|
92
|
+
- 外部依赖(import 的模块、调用的外部方法)
|
|
93
|
+
|
|
94
|
+
4. 识别需要创建的用例类型:
|
|
95
|
+
- 正向用例(每个公开函数至少 1 个)
|
|
96
|
+
- 边界值用例(有 if/elif 分支的函数)
|
|
97
|
+
- 异常路径用例(有 raise/try 的函数)
|
|
98
|
+
- 参数化用例(多输入场景)
|
|
99
|
+
|
|
100
|
+
---
|
|
101
|
+
|
|
102
|
+
## 阶段三:决策 Mock 策略
|
|
103
|
+
|
|
104
|
+
**操作**:根据 test_type 从 config 读取 mock_strategy
|
|
105
|
+
|
|
106
|
+
### unit — mock_strategy=full
|
|
107
|
+
|
|
108
|
+
按依赖类型选择 Mock 方式:
|
|
109
|
+
|
|
110
|
+
| 依赖类型 | 处理策略 |
|
|
111
|
+
|---------|---------|
|
|
112
|
+
| HTTP 请求 | `@patch('requests.get')` 等,预设 return_value |
|
|
113
|
+
| 数据库 | Fake 内存数据库 或 `@patch('db.connect')` |
|
|
114
|
+
| 文件系统 | `tmp_path` fixture |
|
|
115
|
+
| 时间依赖 | `@patch('datetime.now')` |
|
|
116
|
+
| 随机值 | `@patch('random')` 或 `random.seed(42)` |
|
|
117
|
+
| 外部服务 | `@patch('email.send')` 等,验证调用参数 |
|
|
118
|
+
| 环境变量 | `monkeypatch.setenv('KEY', 'val')` |
|
|
119
|
+
| 内部模块 | 优先真实调用,仅 Mock 有副作用的子模块 |
|
|
120
|
+
|
|
121
|
+
### integration — mock_strategy=minimal
|
|
122
|
+
|
|
123
|
+
| 依赖类型 | 处理策略 |
|
|
124
|
+
|---------|---------|
|
|
125
|
+
| 内部模块 | 真实调用,不 Mock |
|
|
126
|
+
| 数据库 | 真实连接(测试库)或 Fake 内存数据库 |
|
|
127
|
+
| 文件系统 | 真实文件操作(使用 tmp_path 清理) |
|
|
128
|
+
| 外部 API | 仅 Mock 不可控的生产 API |
|
|
129
|
+
| 时间/随机 | 可接受真实值,或 seed 固定 |
|
|
130
|
+
|
|
131
|
+
---
|
|
132
|
+
|
|
133
|
+
## 阶段四:创建用例
|
|
134
|
+
|
|
135
|
+
**操作**:
|
|
136
|
+
|
|
137
|
+
### 全新模式
|
|
138
|
+
|
|
139
|
+
1. 使用传入的 `test_dir` 确定测试文件路径:
|
|
140
|
+
```
|
|
141
|
+
test_dir: tests/unit/mypackage/
|
|
142
|
+
源码文件: src/mypackage/MyService.py
|
|
143
|
+
测试文件: tests/unit/mypackage/test_MyService.py
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
2. 按命名规范创建测试文件:
|
|
147
|
+
- 文件名:`test_{ModuleName}.py`
|
|
148
|
+
- 类名:`test_{ModuleName}`(与文件名一致)
|
|
149
|
+
- 方法名:`test_{action}_{expected}`
|
|
150
|
+
- 2 空格缩进
|
|
151
|
+
- 类格式
|
|
152
|
+
|
|
153
|
+
3. 按 AAA 模式编写每个用例:
|
|
154
|
+
- Arrange — 准备数据、Mock、fixture
|
|
155
|
+
- Act — 调用被测方法
|
|
156
|
+
- Assert — 断言结果
|
|
157
|
+
|
|
158
|
+
### 增量模式
|
|
159
|
+
|
|
160
|
+
1. 确定目标测试文件(已存在)
|
|
161
|
+
2. 读取已有文件内容,保留全部已有代码不动
|
|
162
|
+
3. 对照阶段零提取的已有方法名集合,跳过已存在的方法
|
|
163
|
+
4. 在已有测试类中追加新方法:
|
|
164
|
+
- 方法名:`test_{action}_{expected}`(不与已有方法重名)
|
|
165
|
+
- 仍遵循 AAA 模式
|
|
166
|
+
- 复用已有的 import 和 fixture,仅在需要时追加 import
|
|
167
|
+
5. 如果已有文件中没有对应的测试类(源码新增了模块),则在该文件中新增类
|
|
168
|
+
|
|
169
|
+
**通用规则**(两种模式共用):
|
|
170
|
+
|
|
171
|
+
1. 按用例类型创建:
|
|
172
|
+
- 正向用例:正常输入 → 预期输出
|
|
173
|
+
- 边界用例:边界值 → 预期行为
|
|
174
|
+
- 异常用例:非法输入 → `pytest.raises`
|
|
175
|
+
- 参数化用例:`@pytest.mark.parametrize`
|
|
176
|
+
|
|
177
|
+
2. 添加 pytest marker:
|
|
178
|
+
- unit: `@pytest.mark.unit`
|
|
179
|
+
- integration: `@pytest.mark.integration`
|
|
180
|
+
|
|
181
|
+
3. 检查 conftest.py(沿父目录向上查找):
|
|
182
|
+
- 检查 test_dir 下是否有 conftest.py → 有则不修改
|
|
183
|
+
- 检查 test_dir 的父目录(tests/unit/ 或 tests/integration/、tests/)是否有 conftest.py → 有则不创建,提示"父目录已有 conftest.py"
|
|
184
|
+
- 均无 conftest.py → 在 test_dir 下创建,仅含通用 fixture
|
|
185
|
+
- 不导入任何项目特定的三方库
|
|
186
|
+
|
|
187
|
+
**创建示例**:
|
|
188
|
+
|
|
189
|
+
```python
|
|
190
|
+
import pytest
|
|
191
|
+
|
|
192
|
+
@pytest.mark.unit
|
|
193
|
+
class test_MyService:
|
|
194
|
+
|
|
195
|
+
def test_create_with_valid_data(self):
|
|
196
|
+
# Arrange
|
|
197
|
+
service = MyService()
|
|
198
|
+
# Act
|
|
199
|
+
result = service.create({"name": "test"})
|
|
200
|
+
# Assert
|
|
201
|
+
assert result.id is not None
|
|
202
|
+
|
|
203
|
+
@pytest.mark.parametrize("name,valid", [
|
|
204
|
+
("", False),
|
|
205
|
+
(None, False),
|
|
206
|
+
("a" * 1000, False),
|
|
207
|
+
("test", True),
|
|
208
|
+
])
|
|
209
|
+
def test_create_validates_name(self, name, valid):
|
|
210
|
+
service = MyService()
|
|
211
|
+
if valid:
|
|
212
|
+
result = service.create({"name": name})
|
|
213
|
+
assert result is not None
|
|
214
|
+
else:
|
|
215
|
+
with pytest.raises(ValueError):
|
|
216
|
+
service.create({"name": name})
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
---
|
|
220
|
+
|
|
221
|
+
## 阶段五:语法验证
|
|
222
|
+
|
|
223
|
+
**操作**:
|
|
224
|
+
|
|
225
|
+
1. 对每个新创建的测试文件运行语法检查:
|
|
226
|
+
```
|
|
227
|
+
uv run python -m py_compile "<test_file>"
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
2. 语法错误 → 修复后重新验证
|
|
231
|
+
|
|
232
|
+
**决策**:
|
|
233
|
+
|
|
234
|
+
| 条件 | 动作 |
|
|
235
|
+
|------|------|
|
|
236
|
+
| 全部语法正确 | 输出创建结果 |
|
|
237
|
+
| 有语法错误 | 修复后重新验证,最多 3 次 |
|
|
238
|
+
| 修复 3 次仍失败 | 记录错误,跳过该文件 |
|
|
239
|
+
|
|
240
|
+
---
|
|
241
|
+
|
|
242
|
+
## 输出格式
|
|
243
|
+
|
|
244
|
+
```
|
|
245
|
+
## 用例创建报告
|
|
246
|
+
|
|
247
|
+
### 工作模式
|
|
248
|
+
- 模式: [全新模式 / 增量模式]
|
|
249
|
+
- 已有测试文件: X 个(增量模式时显示)
|
|
250
|
+
|
|
251
|
+
### 新增/追加测试文件
|
|
252
|
+
| 文件路径 | 模式 | 新增用例数 | 跳过重复 | 覆盖函数 |
|
|
253
|
+
|---------|------|-----------|---------|---------|
|
|
254
|
+
| tests/unit/mypackage/test_MyService.py | 全新创建 | 8 | 0 | create, validate, parse |
|
|
255
|
+
| tests/unit/mypackage/test_OtherService.py | 增量追加 | 3 | 2 | update, delete |
|
|
256
|
+
|
|
257
|
+
### 用例明细
|
|
258
|
+
| 方法名 | 类型 | 被测函数 |
|
|
259
|
+
|--------|------|---------|
|
|
260
|
+
| test_create_with_valid_data | 正向 | create |
|
|
261
|
+
| test_create_raises_on_empty_name | 异常 | create |
|
|
262
|
+
| test_validate_age_boundary | 边界 | validate |
|
|
263
|
+
|
|
264
|
+
### Mock 使用
|
|
265
|
+
| 依赖类型 | Mock 方式 |
|
|
266
|
+
|---------|---------|
|
|
267
|
+
| HTTP | @patch requests.get |
|
|
268
|
+
| 文件 | tmp_path fixture |
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
### 0 新用例检测
|
|
272
|
+
|
|
273
|
+
阶段四完成后,统计新增用例总数。如果为 0(所有方法均已存在),输出特殊标记:
|
|
274
|
+
|
|
275
|
+
```
|
|
276
|
+
## 用例创建报告
|
|
277
|
+
|
|
278
|
+
### 工作模式
|
|
279
|
+
- 模式: 增量模式
|
|
280
|
+
- 新增用例数: 0
|
|
281
|
+
|
|
282
|
+
### 结论
|
|
283
|
+
- [0 新用例] 所有未覆盖区域已有对应测试,无法通过新增用例提升覆盖率
|
|
284
|
+
- 建议: 检查已有用例质量(断言完整性、边界值覆盖),或确认覆盖率瓶颈原因
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
agent 收到此标记后,应跳过 execute + fix,直接回到 coverage-analyze 重新评分或判定"无改进"退出。
|
|
288
|
+
|
|
289
|
+
## 失败处理
|
|
290
|
+
|
|
291
|
+
| 场景 | 处理方式 |
|
|
292
|
+
|------|---------|
|
|
293
|
+
| 源码文件不存在 | 跳过,记录警告 |
|
|
294
|
+
| 源码无法分析(语法错误) | 跳过,记录错误 |
|
|
295
|
+
| 测试目录不存在 | 自动创建 |
|
|
296
|
+
| 语法验证失败 | 修复重试,最多 3 次 |
|
|
297
|
+
| conftest.py 已存在 | 不修改 |
|
|
298
|
+
| 已有测试文件语法错误 | 跳过该文件的追加,记录警告 |
|
|
299
|
+
| uncovered_areas 为空列表 | 输出"无需新建用例,建议改进已有用例质量",返回给 agent |
|
|
300
|
+
| 新增用例数为 0(全部重复) | 输出"0 新用例"标记,agent 应跳过 execute+fix |
|
|
301
|
+
|
|
302
|
+
## 一定要做
|
|
303
|
+
|
|
304
|
+
1. 必须先扫描已有测试文件,确定工作模式(全新/增量)
|
|
305
|
+
2. 增量模式下必须保留已有代码不动,只追加新方法
|
|
306
|
+
3. 必须根据 test_type 选择对应的 Mock 策略
|
|
307
|
+
4. 如果提供了 testable_classes,必须仅对清单中的类创建测试,跳过不在清单中的类
|
|
308
|
+
5. 如果 uncovered_areas 为 "all"(首轮),必须扫描全部源码文件
|
|
309
|
+
6. 必须遵循命名规范(test_ 前缀文件名和类名)
|
|
310
|
+
7. 必须遵循 AAA 模式
|
|
311
|
+
8. 必须为每个未覆盖函数创建至少 1 个正向用例
|
|
312
|
+
9. 有分支的函数必须创建边界值用例
|
|
313
|
+
10. 有异常处理的函数必须创建异常路径用例
|
|
314
|
+
11. 创建后必须验证语法正确
|
|
315
|
+
12. 必须添加对应的 pytest marker
|
|
316
|
+
13. 遵循 AGENTS.md:2 空格缩进、类格式、文件名=类名
|
|
317
|
+
|
|
318
|
+
## 一定不要做
|
|
319
|
+
|
|
320
|
+
1. 不要修改源码文件
|
|
321
|
+
2. 不要覆盖已有测试文件中的已有方法
|
|
322
|
+
3. 不要创建与现有测试重复的用例(方法名相同的跳过)
|
|
323
|
+
4. 不要在单元测试中使用真实网络/文件/时间调用
|
|
324
|
+
5. 不要修改已存在的 conftest.py
|
|
325
|
+
6. 不要导入项目特定的三方库到 conftest.py
|
|
326
|
+
7. 不要跳过语法验证
|
|
327
|
+
8. 不要使用 Test 前缀的类名(统一用 test_ 前缀)
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
# 测试用例创建规范
|
|
2
|
+
|
|
3
|
+
> 本规范适用于 Python pytest 测试框架,包含通用规范、Python 特定规范、以及单元/集成测试专属规范。
|
|
4
|
+
|
|
5
|
+
## 一、通用规范
|
|
6
|
+
|
|
7
|
+
### AAA 模式(Arrange-Act-Assert)
|
|
8
|
+
|
|
9
|
+
每个测试方法分为三段:
|
|
10
|
+
|
|
11
|
+
```python
|
|
12
|
+
def test_create_with_valid_data(self):
|
|
13
|
+
# Arrange — 准备
|
|
14
|
+
service = MyService()
|
|
15
|
+
data = {"name": "test", "age": 18}
|
|
16
|
+
|
|
17
|
+
# Act — 执行
|
|
18
|
+
result = service.create(data)
|
|
19
|
+
|
|
20
|
+
# Assert — 断言
|
|
21
|
+
assert result.id is not None
|
|
22
|
+
assert result.name == "test"
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
### FIRST 原则
|
|
26
|
+
|
|
27
|
+
| 原则 | 说明 |
|
|
28
|
+
|------|------|
|
|
29
|
+
| Fast | 快速执行(单元测试秒级,集成测试分钟级) |
|
|
30
|
+
| Isolated | 测试间相互独立,不依赖执行顺序 |
|
|
31
|
+
| Repeatable | 可重复运行,结果稳定 |
|
|
32
|
+
| Self-Validating | 自动断言,无需人工判断 |
|
|
33
|
+
| Timely | 与开发同步编写 |
|
|
34
|
+
|
|
35
|
+
### 命名规范
|
|
36
|
+
|
|
37
|
+
| 元素 | 规则 | 示例 |
|
|
38
|
+
|------|------|------|
|
|
39
|
+
| 文件名 | `test_{ModuleName}.py` | `test_MyService.py` |
|
|
40
|
+
| 类名 | `test_{ModuleName}`(与文件名一致) | `test_MyService` |
|
|
41
|
+
| 方法名 | `test_{action}_{expected}` | `test_create_raises_on_empty_input` |
|
|
42
|
+
|
|
43
|
+
方法命名要素:
|
|
44
|
+
- **action**:被测方法名或核心行为(create, send, parse)
|
|
45
|
+
- **expected**:输入条件或预期结果(with_valid_data, raises_on_timeout)
|
|
46
|
+
|
|
47
|
+
### 一个方法一个行为
|
|
48
|
+
|
|
49
|
+
每个测试方法只验证一个行为,不混合多个断言逻辑。
|
|
50
|
+
|
|
51
|
+
## 二、Python/pytest 特定规范
|
|
52
|
+
|
|
53
|
+
### 类格式
|
|
54
|
+
|
|
55
|
+
遵循 AGENTS.md 规则:
|
|
56
|
+
- 2 空格缩进
|
|
57
|
+
- 类风格(非函数式)
|
|
58
|
+
- 文件名与类名一致
|
|
59
|
+
- 一个文件内只定义一个类
|
|
60
|
+
|
|
61
|
+
```python
|
|
62
|
+
import pytest
|
|
63
|
+
|
|
64
|
+
class test_MyService:
|
|
65
|
+
|
|
66
|
+
def test_create_with_valid_data(self):
|
|
67
|
+
# Arrange
|
|
68
|
+
service = MyService()
|
|
69
|
+
# Act
|
|
70
|
+
result = service.create({"name": "test"})
|
|
71
|
+
# Assert
|
|
72
|
+
assert result.id is not None
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### fixture 使用
|
|
76
|
+
|
|
77
|
+
```python
|
|
78
|
+
import pytest
|
|
79
|
+
|
|
80
|
+
class test_MyService:
|
|
81
|
+
|
|
82
|
+
@pytest.fixture
|
|
83
|
+
def service(self):
|
|
84
|
+
return MyService()
|
|
85
|
+
|
|
86
|
+
@pytest.fixture
|
|
87
|
+
def valid_data(self):
|
|
88
|
+
return {"name": "test", "age": 18}
|
|
89
|
+
|
|
90
|
+
def test_create_with_valid_data(self, service, valid_data):
|
|
91
|
+
result = service.create(valid_data)
|
|
92
|
+
assert result.id is not None
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### Mock/Stub 使用
|
|
96
|
+
|
|
97
|
+
```python
|
|
98
|
+
from unittest.mock import Mock, patch
|
|
99
|
+
|
|
100
|
+
class test_MyService:
|
|
101
|
+
|
|
102
|
+
@patch('mypackage.MyService.fetch_remote')
|
|
103
|
+
def test_process_returns_data_from_remote(self, mock_fetch):
|
|
104
|
+
# Stub — 预设返回值
|
|
105
|
+
mock_fetch.return_value = {"key": "value"}
|
|
106
|
+
|
|
107
|
+
service = MyService()
|
|
108
|
+
result = service.process()
|
|
109
|
+
|
|
110
|
+
# Mock — 验证调用行为
|
|
111
|
+
mock_fetch.assert_called_once()
|
|
112
|
+
assert result["key"] == "value"
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### 参数化测试
|
|
116
|
+
|
|
117
|
+
```python
|
|
118
|
+
class test_MyValidator:
|
|
119
|
+
|
|
120
|
+
@pytest.mark.parametrize("input,expected", [
|
|
121
|
+
(18, True),
|
|
122
|
+
(30, True),
|
|
123
|
+
(60, True),
|
|
124
|
+
(17, False),
|
|
125
|
+
(61, False),
|
|
126
|
+
(-1, False),
|
|
127
|
+
])
|
|
128
|
+
def test_validate_age(self, input, expected):
|
|
129
|
+
assert MyValidator.validate_age(input) == expected
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
### 异常断言
|
|
133
|
+
|
|
134
|
+
```python
|
|
135
|
+
class test_MyService:
|
|
136
|
+
|
|
137
|
+
def test_create_raises_on_empty_name(self):
|
|
138
|
+
service = MyService()
|
|
139
|
+
with pytest.raises(ValueError, match="name不能为空"):
|
|
140
|
+
service.create({"name": ""})
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
### pytest marker
|
|
144
|
+
|
|
145
|
+
```python
|
|
146
|
+
@pytest.mark.unit
|
|
147
|
+
class test_MyService:
|
|
148
|
+
# 单元测试
|
|
149
|
+
|
|
150
|
+
@pytest.mark.integration
|
|
151
|
+
class test_LoginFlow:
|
|
152
|
+
# 集成测试
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
## 三、单元测试专属规范(unit)
|
|
156
|
+
|
|
157
|
+
### Mock 策略:full — 全部 Mock 外部依赖
|
|
158
|
+
|
|
159
|
+
| 依赖类型 | 处理策略 | 示例 |
|
|
160
|
+
|---------|---------|------|
|
|
161
|
+
| HTTP 请求 | Mock:patch 请求方法,预设返回值 | `@patch('requests.get')` |
|
|
162
|
+
| 数据库操作 | Fake:内存数据库 或 patch 连接层 | `@patch('db.connect')` |
|
|
163
|
+
| 文件系统 | Fake:使用 `tmp_path` fixture | `def test_read(self, tmp_path)` |
|
|
164
|
+
| 时间依赖 | Mock:patch datetime.now | `@patch('datetime.now')` |
|
|
165
|
+
| 随机值 | Mock:patch random 或设 seed | `random.seed(42)` |
|
|
166
|
+
| 外部服务 | Mock:patch 发送方法 | `@patch('email.send')` |
|
|
167
|
+
| 环境变量 | Stub:monkeypatch.setenv | `monkeypatch.setenv('KEY', 'val')` |
|
|
168
|
+
|
|
169
|
+
### 用例覆盖要求
|
|
170
|
+
|
|
171
|
+
| 源码特征 | 必须创建的用例 |
|
|
172
|
+
|---------|---------------|
|
|
173
|
+
| 每个公开函数 | 至少 1 个正向用例 |
|
|
174
|
+
| 含 if/elif 分支 | 边界值用例(每个分支至少 1 个) |
|
|
175
|
+
| 含 raise/try-except | 异常路径用例(pytest.raises) |
|
|
176
|
+
| 多输入场景 | 参数化用例(@pytest.mark.parametrize) |
|
|
177
|
+
| 有返回值 | 断言返回值类型和内容 |
|
|
178
|
+
|
|
179
|
+
### 禁止事项
|
|
180
|
+
|
|
181
|
+
- 禁止真实网络调用
|
|
182
|
+
- 禁止操作真实文件系统
|
|
183
|
+
- 禁止依赖真实时间
|
|
184
|
+
- 禁止依赖随机值
|
|
185
|
+
- 禁止测试间依赖(共享状态)
|
|
186
|
+
- 禁止硬编码路径
|
|
187
|
+
|
|
188
|
+
## 四、集成测试专属规范(integration)
|
|
189
|
+
|
|
190
|
+
### Mock 策略:minimal — 最少 Mock,优先真实调用
|
|
191
|
+
|
|
192
|
+
| 依赖类型 | 处理策略 |
|
|
193
|
+
|---------|---------|
|
|
194
|
+
| 内部模块 | 真实调用,不 Mock |
|
|
195
|
+
| 数据库 | 真实连接(测试库)或 Fake 内存数据库 |
|
|
196
|
+
| 文件系统 | 真实文件操作(使用 tmp_path 清理) |
|
|
197
|
+
| 外部 API | 仅 Mock 不可控的生产 API,测试环境 API 真实调用 |
|
|
198
|
+
| 时间/随机 | 可接受真实值,或 seed 固定 |
|
|
199
|
+
|
|
200
|
+
### 用例组织
|
|
201
|
+
|
|
202
|
+
- 按 **业务场景** 组织用例,不按函数
|
|
203
|
+
- 每个场景覆盖完整流程:输入 → 处理 → 输出
|
|
204
|
+
- 验证端到端数据流完整性
|
|
205
|
+
|
|
206
|
+
```python
|
|
207
|
+
@pytest.mark.integration
|
|
208
|
+
class test_OrderFlow:
|
|
209
|
+
|
|
210
|
+
def test_create_order_and_send_notification(self, tmp_path):
|
|
211
|
+
# 场景:创建订单并发送通知
|
|
212
|
+
# Arrange
|
|
213
|
+
db = Database(test_url)
|
|
214
|
+
service = OrderService(db=db)
|
|
215
|
+
|
|
216
|
+
# Act
|
|
217
|
+
order = service.create_order(user_id=1, item="book")
|
|
218
|
+
notification = service.send_notification(order.id)
|
|
219
|
+
|
|
220
|
+
# Assert — 验证完整数据流
|
|
221
|
+
assert order.id is not None
|
|
222
|
+
assert order.status == "created"
|
|
223
|
+
assert notification.order_id == order.id
|
|
224
|
+
assert notification.sent is True
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
### fixture 使用
|
|
228
|
+
|
|
229
|
+
- 使用 `scope="class"` 的 fixture 共享 setup
|
|
230
|
+
- teardown 负责清理测试数据
|
|
231
|
+
- 数据准备放在 fixture 中,不硬编码在测试方法内
|
|
232
|
+
|
|
233
|
+
## 五、稳定性保障规则
|
|
234
|
+
|
|
235
|
+
| 规则 | 说明 |
|
|
236
|
+
|------|------|
|
|
237
|
+
| 禁止真实网络 | 单元测试所有 HTTP 必须 Mock |
|
|
238
|
+
| 禁止真实文件 | 文件操作使用 tmp_path fixture |
|
|
239
|
+
| 禁止真实时间 | 时间依赖必须 patch |
|
|
240
|
+
| 禁止真实随机 | 随机值必须 patch 或 seed |
|
|
241
|
+
| 禁止真实外部服务 | 邮件/消息队列等必须 Mock(单元测试) |
|
|
242
|
+
| 禁止测试间依赖 | 每个测试独立,不依赖执行顺序 |
|
|
243
|
+
| 禁止硬编码路径 | 使用 fixture 或相对路径 |
|
|
244
|
+
| fixture 作用域 | class 级用 scope="class",函数级用默认 |
|