Skip to content
项目
群组
代码片段
帮助
正在加载...
帮助
为 GitLab 提交贡献
登录
切换导航
U
ubains-module-test
项目
项目
详情
活动
周期分析
仓库
仓库
文件
提交
分支
标签
贡献者
分枝图
比较
统计图
议题
1
议题
1
列表
看板
标记
里程碑
合并请求
0
合并请求
0
CI / CD
CI / CD
流水线
作业
计划
统计图
Wiki
Wiki
代码片段
代码片段
成员
成员
折叠边栏
关闭边栏
活动
分枝图
统计图
创建新议题
作业
提交
议题看板
打开侧边栏
郑晓兵
ubains-module-test
Commits
ed7ea006
提交
ed7ea006
authored
3月 10, 2026
作者:
陈泽健
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
docs(prd): 删除用例生成相关PRD文档,输出AI用例完善的需求文档实现功能。
- 移除根据PRD文档生成测试用例的需求文档 - 删除用例生成计划执行文档 - 清理相关的PRD规范和实施计划内容
上级
e50d943e
隐藏空白字符变更
内嵌
并排
正在显示
49 个修改的文件
包含
8038 行增加
和
1318 行删除
+8038
-1318
.env.example
AuxiliaryTool/AIPerfectedTestCases/.env.example
+39
-0
.gitignore
AuxiliaryTool/AIPerfectedTestCases/.gitignore
+37
-0
README.md
AuxiliaryTool/AIPerfectedTestCases/README.md
+314
-0
PRD_权限控制_数据扩展补充.md
...Tool/AIPerfectedTestCases/config/开发PRD/PRD_权限控制_数据扩展补充.md
+28
-0
PRD_权限控制_权限管理页面新增开发.md
.../AIPerfectedTestCases/config/开发PRD/PRD_权限控制_权限管理页面新增开发.md
+79
-0
PRD_权限组管理_基础权限组_调整基础角色基础权限组.md
...ctedTestCases/config/开发PRD/PRD_权限组管理_基础权限组_调整基础角色基础权限组.md
+36
-0
PRD_权限组管理_增删改查_权限组功能开发设计.md
...rfectedTestCases/config/开发PRD/PRD_权限组管理_增删改查_权限组功能开发设计.md
+147
-0
PRD_权限组管理_权限结构_调整权限数据结构.md
...erfectedTestCases/config/开发PRD/PRD_权限组管理_权限结构_调整权限数据结构.md
+16
-0
PRD_权限组管理_添加权限组_给基础角色添加基本权限组.md
...tedTestCases/config/开发PRD/PRD_权限组管理_添加权限组_给基础角色添加基本权限组.md
+45
-0
PRD_权限组管理_详情查看_添加权限组详情查看.md
...rfectedTestCases/config/开发PRD/PRD_权限组管理_详情查看_添加权限组详情查看.md
+24
-0
新统一平台权限管理测试用例.xlsx
...yTool/AIPerfectedTestCases/config/测试用例/新统一平台权限管理测试用例.xlsx
+0
-0
requirements.txt
AuxiliaryTool/AIPerfectedTestCases/requirements.txt
+19
-0
run.py
AuxiliaryTool/AIPerfectedTestCases/run.py
+341
-0
__init__.py
AuxiliaryTool/AIPerfectedTestCases/src/__init__.py
+10
-0
ai_service_manager.py
AuxiliaryTool/AIPerfectedTestCases/src/ai_service_manager.py
+325
-0
case_generator.py
AuxiliaryTool/AIPerfectedTestCases/src/case_generator.py
+442
-0
claude_cli_adapter.py
AuxiliaryTool/AIPerfectedTestCases/src/claude_cli_adapter.py
+297
-0
config.py
AuxiliaryTool/AIPerfectedTestCases/src/config.py
+177
-0
deduplicator.py
AuxiliaryTool/AIPerfectedTestCases/src/deduplicator.py
+289
-0
document_reader.py
AuxiliaryTool/AIPerfectedTestCases/src/document_reader.py
+338
-0
gap_detector.py
AuxiliaryTool/AIPerfectedTestCases/src/gap_detector.py
+368
-0
main.py
AuxiliaryTool/AIPerfectedTestCases/src/main.py
+381
-0
report_generator.py
AuxiliaryTool/AIPerfectedTestCases/src/report_generator.py
+373
-0
test_case_analyzer.py
AuxiliaryTool/AIPerfectedTestCases/src/test_case_analyzer.py
+504
-0
新统一平台自动化部署操作指导_英文版.docx
...toOptimizationCalibration/reports/新统一平台自动化部署操作指导_英文版.docx
+0
-0
AI服务初始化失败_问题处理.md
Docs/PRD/AI用例完善/问题处理/AI服务初始化失败_问题处理.md
+18
-0
AI服务初始化失败_问题处理_计划执行.md
Docs/PRD/AI用例完善/问题处理/AI服务初始化失败_问题处理_计划执行.md
+135
-0
AI调用失败Extradata_问题处理.md
Docs/PRD/AI用例完善/问题处理/AI调用失败Extradata_问题处理.md
+25
-0
AI调用失败Extradata_问题处理_计划执行.md
Docs/PRD/AI用例完善/问题处理/AI调用失败Extradata_问题处理_计划执行.md
+206
-0
API模式执行提示调用失败_问题处理.md
Docs/PRD/AI用例完善/问题处理/API模式执行提示调用失败_问题处理.md
+24
-0
API模式执行提示调用失败_问题处理_计划执行.md
Docs/PRD/AI用例完善/问题处理/API模式执行提示调用失败_问题处理_计划执行.md
+165
-0
代码运行报错unexpectedindent_问题处理.md
Docs/PRD/AI用例完善/问题处理/代码运行报错unexpectedindent_问题处理.md
+22
-0
代码运行报错unexpectedindent_问题处理_计划执行.md
Docs/PRD/AI用例完善/问题处理/代码运行报错unexpectedindent_问题处理_计划执行.md
+58
-0
代码运行提示API权限不足_问题处理.md
Docs/PRD/AI用例完善/问题处理/代码运行提示API权限不足_问题处理.md
+20
-0
代码运行提示API权限不足_问题处理_计划执行.md
Docs/PRD/AI用例完善/问题处理/代码运行提示API权限不足_问题处理_计划执行.md
+462
-0
代码运行提示ClaudeCLT不可用_问题处理.md
Docs/PRD/AI用例完善/问题处理/代码运行提示ClaudeCLT不可用_问题处理.md
+16
-0
代码运行提示ClaudeCLT不可用_问题处理_计划执行.md
Docs/PRD/AI用例完善/问题处理/代码运行提示ClaudeCLT不可用_问题处理_计划执行.md
+152
-0
强制代码使用API模式失败_问题处理.md
Docs/PRD/AI用例完善/问题处理/强制代码使用API模式失败_问题处理.md
+23
-0
强制代码使用API模式失败_问题处理_计划执行.md
Docs/PRD/AI用例完善/问题处理/强制代码使用API模式失败_问题处理_计划执行.md
+93
-0
生成完善用例完全没有新增内容_问题处理.md
Docs/PRD/AI用例完善/问题处理/生成完善用例完全没有新增内容_问题处理.md
+160
-0
生成完善用例完全没有新增内容_问题处理_计划执行.md
Docs/PRD/AI用例完善/问题处理/生成完善用例完全没有新增内容_问题处理_计划执行.md
+444
-0
生成测试用例失败_问题处理.md
Docs/PRD/AI用例完善/问题处理/生成测试用例失败_问题处理.md
+39
-0
生成测试用例失败_问题处理_计划执行.md
Docs/PRD/AI用例完善/问题处理/生成测试用例失败_问题处理_计划执行.md
+129
-0
AI服务调用方式补充说明.md
Docs/PRD/AI用例完善/需求文档/AI服务调用方式补充说明.md
+131
-0
AI用例完善需求文档.md
Docs/PRD/AI用例完善/需求文档/AI用例完善需求文档.md
+73
-0
AI用例完善需求文档_计划执行.md
Docs/PRD/AI用例完善/需求文档/AI用例完善需求文档_计划执行.md
+1014
-0
_PRD_根据PRD文档生成测试用例.md
Docs/PRD/用例生成/_PRD_根据PRD文档生成测试用例.md
+0
-310
_PRD_根据PRD文档生成测试用例_计划执行.md
Docs/PRD/用例生成/_PRD_根据PRD文档生成测试用例_计划执行.md
+0
-1008
自动生成功能报告需求文档.md
Docs/PRD/自动生成功能测试报告/自动生成功能报告需求文档.md
+0
-0
没有找到文件。
AuxiliaryTool/AIPerfectedTestCases/.env.example
0 → 100644
浏览文件 @
ed7ea006
# AI完善测试用例工具 - 环境变量配置文件
#
# 说明:复制此文件为 .env 并填入实际的配置值
# .env 文件已加入 .gitignore,不会被提交到代码仓库
# ==================== Anthropic API 配置 ====================
# 获取API密钥:https://console.anthropic.com/
ANTHROPIC_API_KEY=sk-ant-api03-...
# ==================== API配置(可选)====================
# API基础URL(用于代理服务)
# 官方API可留空或设置为:https://api.anthropic.com
# CCSwitch等代理服务需设置对应endpoint
# API_BASE_URL=https://api.anthropic.com
# ==================== AI服务模式配置 ====================
# AI服务模式选择(可选)
# auto: 自动检测(默认)- 优先使用API,无API密钥时尝试CLI
# api: 强制使用Anthropic API(需要设置ANTHROPIC_API_KEY)
# cli: 强制使用Claude CLI(需要安装:npm install -g @anthropic-ai/claude-code)
# AI_SERVICE_MODE=api
# ==================== 日志配置 ====================
# 日志级别:DEBUG, INFO, WARNING, ERROR
LOG_LEVEL=INFO
# ==================== AI配置(可选)====================
# AI模型:claude-sonnet-4-6, claude-opus-4-6
# AI_MODEL=claude-sonnet-4-6
# AI最大token数
# AI_MAX_TOKENS=8192
# AI温度参数(0-1,越低越稳定)
# AI_TEMPERATURE=0.3
# ==================== 覆盖率目标(可选)====================
# 覆盖率目标(0-1)
# COVERAGE_TARGET=0.9
AuxiliaryTool/AIPerfectedTestCases/.gitignore
0 → 100644
浏览文件 @
ed7ea006
# AI完善测试用例工具 - Git忽略文件
# 环境变量配置(包含API密钥)
.env
# Python缓存
__pycache__/
*.py[cod]
*$py.class
*.so
.Python
# 虚拟环境
venv/
env/
ENV/
# IDE配置
.vscode/
.idea/
*.swp
*.swo
# 日志文件
logs/*.log
# 输出文件
Perfected/*.xlsx
reports/*.docx
# 临时文件
*.tmp
.DS_Store
Thumbs.db
# 测试文件
*.pyc
AuxiliaryTool/AIPerfectedTestCases/README.md
0 → 100644
浏览文件 @
ed7ea006
# AI完善测试用例工具
## 项目简介
本工具通过结合已有测试用例、需求文档、开发需求PRD文档,利用AI能力对测试用例进行查漏补缺,完善测试用例内容,确保测试用例覆盖全面且符合规范要求。
### 主要功能
-
**文档读取**
:自动读取需求文档(.docx)、开发PRD文档(.md)、测试用例(.xlsx)
-
**功能点提取**
:使用AI从需求文档中提取完整的功能点列表
-
**覆盖率分析**
:分析现有测试用例的覆盖情况
-
**缺失检测**
:检测功能点、场景、边界条件的缺失
-
**用例生成**
:使用AI自动生成缺失的测试用例
-
**去重处理**
:智能去重,确保测试用例不重复
-
**报告生成**
:生成详细的差异性报告
### 覆盖率目标
-
**场景覆盖率目标**
:90%
---
## 项目结构
```
AuxiliaryTool/AIPerfectedTestCases/
├── src/ # 源代码目录
│ ├── __init__.py
│ ├── config.py # 配置模块
│ ├── document_reader.py # 文档读取模块
│ ├── test_case_analyzer.py # 测试用例分析模块
│ ├── gap_detector.py # 缺失检测模块
│ ├── case_generator.py # 用例生成模块
│ ├── deduplicator.py # 去重模块
│ ├── report_generator.py # 报告生成模块
│ └── main.py # 主入口模块
├── config/ # 配置文件目录
│ ├── 需求文档/
│ │ └── *.docx # 需求文档
│ ├── 开发PRD/
│ │ └── *.md # 开发PRD文档
│ └── 测试用例/
│ └── 新统一平台权限管理测试用例.xlsx
├── Perfected/ # 输出目录
│ └── 新统一平台权限管理测试用例_完善版本.xlsx
├── reports/ # 报告输出目录
│ └── 新统一平台权限管理测试用例_差异性报告.docx
├── logs/ # 日志目录
├── run.py # 入口脚本
├── requirements.txt # 依赖包列表
└── README.md # 说明文档
```
---
## 安装说明
### 环境要求
-
Python 3.10+
-
Anthropic API密钥
### 安装步骤
1.
**进入项目目录**
```
bash
cd
AuxiliaryTool/AIPerfectedTestCases
```
2.
**安装依赖**
```
bash
pip
install
-r
requirements.txt
```
3.
**设置API密钥**
**方式一:使用 .env 文件(推荐)**
复制
`.env.example`
为
`.env`
,然后编辑
`.env`
文件:
```
bash
# 复制模板文件
cp
.env.example .env
# 编辑 .env 文件,填入你的API密钥
# ANTHROPIC_API_KEY=sk-ant-api03-xxxxxxxxxxxxxxxx
```
**方式二:环境变量(临时)**
**Windows:**
```
bash
set
ANTHROPIC_API_KEY
=
your-api-key-here
```
**Linux/Mac:**
```
bash
export
ANTHROPIC_API_KEY
=
your-api-key-here
```
---
## 使用说明
### 基础用法
```
bash
# 使用默认配置运行
python run.py
```
### 命令行参数
| 参数 | 说明 | 示例 |
|------|------|------|
|
`--log-level`
| 日志级别(DEBUG/INFO/WARNING/ERROR) |
`--log-level DEBUG`
|
|
`--show-info`
| 显示项目信息 |
`--show-info`
|
|
`--check-only`
| 仅检查环境,不执行处理 |
`--check-only`
|
|
`--help`
| 显示帮助信息 |
`--help`
|
### 使用示例
```
bash
# 正常运行
python run.py
# 启用详细日志
python run.py
--log-level
DEBUG
# 仅检查环境
python run.py
--check-only
# 显示项目信息
python run.py
--show-info
```
---
## 输入文件说明
### 1. 需求文档(.docx)
-
**位置**
:
`config/需求文档/`
-
**格式**
:Word文档
-
**内容**
:功能需求描述
### 2. 开发PRD文档(.md)
-
**位置**
:
`config/开发PRD/`
-
**格式**
:Markdown文档
-
**内容**
:功能实现细节、边界条件
### 3. 测试用例(.xlsx)
-
**位置**
:
`config/测试用例/`
-
**格式**
:Excel表格
-
**列定义**
:
| 列名 | 说明 |
|------|------|
| 序列号 | 序号,用于排序 |
| 功能模块 | 功能模块名称 |
| 用例编号 | 用例编号 |
| 功能描述 | 功能描述 |
| 用例等级 | 用例等级(P0/P1/P2/P3) |
| 功能编号 | 功能编号 |
| 用例名称 | 用例名称 |
| 预置条件 | 测试预置条件 |
| STEP | 步骤描述 |
| JSON | 自动化测试用例(暂不涉及) |
| 预期结果 | 预期结果 |
| 测试结果 | 测试结果 |
| 测试频次 | 测试频次 |
| 日志/截图/照片 | 日志/截图/照片 |
| 备注 | 备注 |
---
## 输出文件说明
### 1. 完善后的测试用例
-
**位置**
:
`Perfected/新统一平台权限管理测试用例_完善版本.xlsx`
-
**内容**
:原始用例 + 新增用例
-
**标记**
:新增用例有浅蓝色背景
### 2. 差异性报告
-
**位置**
:
`reports/新统一平台权限管理测试用例_差异性报告.docx`
-
**内容**
:
-
功能点缺失的测试用例列表
-
场景覆盖不足的测试用例列表
-
边界条件不足的测试用例列表
-
去重处理结果
-
用例数量增长统计
-
覆盖率百分比统计
-
总结和建议
---
## 处理流程
```
1. 读取文档
├─ 读取需求文档(.docx)
├─ 读取PRD文档(.md)
└─ 读取测试用例(.xlsx)
2. 提取功能点
└─ 使用AI从需求文档中提取功能点列表
3. 分析覆盖率
└─ 分析现有测试用例的覆盖情况
4. 检测缺失
├─ 功能点缺失
├─ 场景覆盖不足
└─ 边界条件不足
5. 生成新用例
├─ 为功能点缺失生成用例
├─ 为场景覆盖不足生成用例
└─ 为边界条件不足生成用例
6. 去重处理
└─ 检测并去除重复用例
7. 保存结果
├─ 保存完善后的测试用例
└─ 生成差异性报告
```
---
## 配置说明
配置文件位于
`src/config.py`
,可修改以下参数:
### AI配置
```
python
AI_MODEL
=
"claude-sonnet-4-6"
# AI模型选择
AI_MAX_TOKENS
=
8192
# 最大token数
AI_TEMPERATURE
=
0.3
# 温度参数
```
### 覆盖率目标
```
python
COVERAGE_TARGET
=
0.9
# 90%覆盖率目标
```
### 测试用例配置
```
python
CASE_LEVELS
=
[
"P0"
,
"P1"
,
"P2"
,
"P3"
]
# 用例等级
DEFAULT_CASE_LEVEL
=
"P1"
# 默认等级
DEFAULT_TEST_FREQUENCY
=
"每版本"
# 默认测试频次
```
---
## 注意事项
1.
**API密钥安全**
:请妥善保管Anthropic API密钥,不要泄露
2.
**成本控制**
:大量API调用可能产生费用,建议先小规模测试
3.
**人工审核**
:AI生成的测试用例需要人工审核确认
4.
**文件占用**
:运行时请确保测试用例文件没有被其他程序打开
5.
**网络连接**
:需要稳定的网络连接调用AI服务
---
## 常见问题
### Q1: 提示API密钥未设置怎么办?
请设置环境变量:
-
Windows:
`set ANTHROPIC_API_KEY=your-key-here`
-
Linux/Mac:
`export ANTHROPIC_API_KEY=your-key-here`
### Q2: 如何获取Anthropic API密钥?
访问 Anthropic 官网:https://www.anthropic.com/
### Q3: 支持哪些AI模型?
目前支持 Claude Sonnet 4.6 和 Claude Opus 4.6,可在
`config.py`
中修改。
### Q4: 生成的测试用例可以直接使用吗?
建议对AI生成的测试用例进行人工审核和修改后使用。
### Q5: 如何调整覆盖率目标?
修改
`src/config.py`
中的
`COVERAGE_TARGET`
参数。
---
## 版本历史
| 版本 | 日期 | 说明 |
|------|------|------|
| v1.0.0 | 2026-03-10 | 初始版本 |
---
## 优化功能回填
-
[
]
支持自定义AI提示词模板
-
[
]
支持增量更新(只处理变更的部分)
-
[
]
支持多轮对话优化用例
-
[
]
支持导出为多种格式
-
[
]
添加用例质量评分
-
[
]
支持批量处理多个项目
-
[
]
支持GUI界面
AuxiliaryTool/AIPerfectedTestCases/config/开发PRD/PRD_权限控制_数据扩展补充.md
0 → 100644
浏览文件 @
ed7ea006
# 权限数据扩展补充
## 概述
1.
**配置项数据地址**
:
`Docs\Temp\licence.js`
2.
**权限控制数据地址**
:
`Docs\Temp\permission.json`
## 任务
1.
✅ 根据配置项数据补充权限控制数据
2.
✅ 补充权限控制数据的国际化
3.
✅ 补充相关说明文档
### 补充权限控制数据要求
1.
配置项数据和权限控制数据中都包含有
`xx_yyyy_list`
数据,帮我根据配置项数据并参考权限控制数据原数据补充权限控制数据,每个都包含基础的
`view、create、edit、delete`
2.
配置项数据中只要是
`xx_yyyy_list`
里的数据,都要补充到权限控制数据中
3.
当补充完数据后,需要确认一下是不是每一个都包含在权限控制数据里面了
### 补充权限控制数据的国际化要求
1.
配置项数据中有着一些注释比如
`set_room_enable`
的注释为"区域管理",这个将代表着
`set_room_list`
的为"区域管理",子集
`office_manage`
也有着
`办公室管理`
的注释
2.
帮我根据注释以及上面的规律,找到国际化文件中的
`permissionClass`
将
`xx_yyyy_enable`
的注释作为
`xx_yyyy_list`
的国际化添加进行,将子集的注释补充到
`permission`
中,并实现国际化
3.
当补充完国际化后,需要确认一下国际化中的
`permissionClass`
是否包含了每一个
`xx_yyyy_enable`
的注释作为
`xx_yyyy_list`
的value,
`permission`
中包含每一个子集的注释
### 相关说明文档补充要求
1.
**文档位置**
:
`Docs\Temp\权限配置README.md`
2.
由于权限控制数据是一个json文件,为了说明相关的配置有何功能,所以在文档进行说明,我要你将配置项和相关的文件说明整合到文档中
## 国际化
当涉及到国际化时,遵循
`Docs\PRD\_PRD_规范文档_国际化.md`
规范
\ No newline at end of file
AuxiliaryTool/AIPerfectedTestCases/config/开发PRD/PRD_权限控制_权限管理页面新增开发.md
0 → 100644
浏览文件 @
ed7ea006
# 权限管理页面新增开发
## 📋 概述
1.
权限管理页面地址:
`src\views\Backend\Admin\PermissionManage\index.vue`
2.
相关API文档地址:
`Docs\Api\API_权限组管理接口文档.md`
3.
新增/修改权限组页面地址:
`src\views\Backend\Admin\PermissionManage\components\AddEditPage\index.vue`
4.
权限绑定弹窗地址:
`src\views\Backend\Admin\PermissionManage\components\BindDialog\index.vue`
5.
自定义权限控制指令v-permission地址:
`src\utils\permission.js`
6.
权限组弹窗权限勾选组件:
`src\views\Backend\Admin\PermissionManage\components\PermissionConfig\index.vue`
## 🎯 任务
1.
✅ 在权限管理页面遵循
`Docs\PRD\_PRD_规范文档_新建页面.md`
并标准化初始化页面,在
`src\router.js`
注册路由
2.
✅ 参考页面
`src\views\Backend\Admin\Role\index.vue`
的设计在初始化后的权限管理页新增功能
3.
✅ 将权限管理页面的新增/修改权限组弹窗和绑定弹窗分别抽离成组件放置在
`src\views\Backend\Admin\PermissionManage\components`
中,注意数据的传输,并优化一下样式
4.
✅ 实现权限组添加权限
5.
✅ 在新增/修改权限组弹窗继续实现任务4的要求
6.
✅ 帮我参考API接口文档,实现权限组的增删改查、禁用/启用功能,绑定功能先不实现
7.
✅ 进入权限管理页面时,帮我请求API接口文档的接口
`/permissionGroup/getAllPermissions`
获取所有配置项,然后将
`src\views\Backend\Admin\PermissionManage\components\PermissionConfig\index.vue`
的permissionList替换为接口请求到的真实数据
8.
✅ 重构权限绑定弹窗及补充功能实现
9.
✅ 数据结构调整
10.
✅ 用户反馈说新增/修改权限组弹窗和权限绑定弹窗这两个弹窗数据比较多,使用弹窗显示不全,帮我改成页面组件,不需要新建router路由,只需要改为页面级组件,并遵循
`Docs\PRD\_PRD_规范文档_新建页面.md`
与使用ui-ux-pro-max优化页面样式,要求样式紧凑一点,尽可能显示多的页面数据
11.
帮我参考新增/修改权限组页面样式,实现权限查看功能与页面
### 权限页面基础功能
1.
实现功能时提供了API接口,但先不进行对接,先在前端用虚拟数据实现并预留操作方法等,等我要接口对接时再进行接口对接
2.
支持模糊搜索,支持基础的增删改查
3.
el-table的主要显示字段获取参考相关API文档的"分页查询权限组"接口,显示字段groupName、isEnable、createTime,支持多选,支持分页,操作有修改、删除、绑定
### 权限组添加权限要求
1.
实现功能时提供了API接口,但先不进行对接,先在前端用虚拟数据实现并预留操作方法等,等我要接口对接时再进行接口对接
2.
我在
`src\constant\permissionList.js`
模拟了权限数据,帮我使用el-checkbox进行显示
3.
文本显示逻辑:在国际化文件中新建permission子集,通过key值匹配显示文本:例如view=i18n文件中的
`permission.view`
4.
页面显示:要同时显示名称和勾选框,比如
```
json
functionType_
82
:
{
view
:
0
,
create
:
0
,
update
:
0
,
delete
:
0
,
},
```
要显示为"办公室管理:查看 新增 编辑 删除"
### 重构权限绑定弹窗及补充功能实现要求
1.
弹窗页面重构,当前的设计不符合产品的功能要求,具体功能要求:权限组支持同时绑定多个用户、角色、部门,取消权限配置功能
2.
页面样式自由发挥,符合系统主题即可,优先使用UI/UX Pro Max
3.
参考
`@src/views/Backend/Account/User/index.vue:930-984 `
实现用户数据请求获取
4.
参考
`@src/views/Backend/Account/User/index.vue:986-1010 `
实现角色数据的请求获取
5.
参考
`@src/views/Backend/Account/User/index.vue:263-273 `
和
`@src/views/Backend/Account/User/index.vue:1055-1068 `
实现部门数据的请求获取
6.
根据权限api文档的
`/permissionGroup/bindRelation`
实现权限绑定
## 任务执行要求
-
在执行任务前,判断当前文件根目录下有没有"当前文件+'_计划执行.md'"文件,例如"PRD_实时转录_页面整合.md"与"PRD_实时转录_页面整合_计划执行.md",没有则新建一个
-
在执行任务中,将将要执行的计划补充在"当前文件+'_计划执行.md'"文件中,执行的任务以"当前文件+'_计划执行.md'"为主
-
在执行任务后,将任务执行的结果返回补充到"当前文件+'_计划执行.md'"文件中,需要写明实现了什么,以及有什么优化项或者有什么风险等
## 问题排查要求
-
在问题排查前,判断当前文件根目录下有没有"当前文件+'_分析执行.md'"文件,例如"PRD_实时转录_页面整合.md"与"PRD_实时转录_页面整合_分析执行.md",没有则新建一个
-
在问题排查中,将将要执行的操作补充在"当前文件+'_分析执行.md'"文件中,执行的操作以"当前文件+'_分析执行.md'"为主
-
在问题排查后,将问题修复的结果返回补充到"当前文件+'_分析执行.md'"文件中,需要写明实现了什么,以及有什么优化项或者有什么风险等
## 过程总结返填
-
在执行任务时,先了解相关的页面与方法,先看看有什么不清楚的或者需要我确认的,先找我确认,再进行代码开发
-
在我确认完后,需要把我确认的东西反填到当前文档中,确保信息对齐
## 代码规范
-
严格按照
`Docs\PRD\_PRD_规范文档_代码规范.md`
## 文档规范
-
严格按照
`Docs\PRD\_PRD_规范文档_文档规范.md`
## 测试验证
-
严格按照
`Docs\PRD\_PRD_规范文档_测试规范.md`
\ No newline at end of file
AuxiliaryTool/AIPerfectedTestCases/config/开发PRD/PRD_权限组管理_基础权限组_调整基础角色基础权限组.md
0 → 100644
浏览文件 @
ed7ea006
# 需求文档
## 概述
**文件**
-
**初始化权限组方法**
:com.ubains.meeting.system.service.impl.PermissionGroupServiceImpl.initBasePermissionGroupsForAllCompanies
-
**数据库初始化文件**
:com/ubains/meeting/common/AdjustmentDb.java
-
**原基础角色基本权限组开发相关文件**
:
-
**原始需求文档**
:Docs/PRD/系统管理/权限管理/PRD_权限组管理_添加权限组_给基础角色添加基本权限组.md
-
**计划执行文档**
:Docs/PRD/系统管理/权限管理/PRD_权限组管理_添加权限组_给基础角色添加基本权限组_计划执行.md
### 背景
原来开发的基础角色和基本权限组有点问题,需要优化调整一下:
1.
基础角色中有个id为2的Admin基础角色,该角色与后续添加的系统管理员冲突了,他们本质应该是同一个
2.
id为2的Admin基础角色没有基本权限组
## 任务
-
参考
`原基础角色基本权限组开发相关文件`
,了解之前的开发逻辑
-
`数据库初始化文件`
从第615行到第668行,调整基础角色
-
id为6的系统管理员角色不要了
-
安全管理员和审计管理员的id往前移1位,即7->6(安全管理员),8->7(审计管理员)
-
`初始化权限组方法`
调整权限组和角色的绑定
-
系统管理员的权限组绑定id为2的原Admin基础角色
-
绑定安全管理员和审计管理员时设置id往前移1位,即7->6(安全管理员),8->7(审计管理员)
-
先给出计划执行方案文档,将文档创建在
`Docs/PRD/+同路径+/同名文件+_计划执行.md`
,等审批完文档再由我考虑是否按计划执行文档来执行
-
生成的计划执行方案文档需添加任务:生成ai输出的总结内容到
`Docs/Doc/+同路径+/Doc_+同名文件(不包含前缀PRD_)+的总结.md`
## 规范
严格执行以下文档
#### 代码规范
-
Docs/PRD/_PRD_规范文档_代码规范.md
#### 文档规范
-
Docs/PRD/_PRD_规范文档_文档规范.md
#### 测试规范
-
Docs/PRD/_PRD_规范文档_测试规范.md
\ No newline at end of file
AuxiliaryTool/AIPerfectedTestCases/config/开发PRD/PRD_权限组管理_增删改查_权限组功能开发设计.md
0 → 100644
浏览文件 @
ed7ea006
## Plan: 权限组管理功能设计
本方案设计一个基于权限组的RBAC权限管理系统。通过
`sys_permission_group`
权限组表和三个独立关联表(部门、角色、用户),实现权限组的增删改查及绑定关系。删除采用物理删除并级联清理关联数据,权限合并采用并集策略。
### 数据库设计
**1. 权限组表 `sys_permission_group`**
| 字段 | 类型 | 说明 |
|------------------|--------------|------------------------------------------------------|
|
`group_id`
| bigint | 主键,自增 |
|
`group_name`
| varchar(100) | 权限组名称 |
|
`permissions`
| text | 权限配置JSON,如
`{"permission1":{"create":1,"delete":0}}`
|
|
`company_number`
| varchar(64) | 公司编号(多租户) |
|
`is_enable`
| int | 是否启用(0:禁用,1:启用) |
|
`is_base`
| int | 是否基础权限组(0:否,1:是) |
|
`create_time`
| datetime | 创建时间 |
|
`update_time`
| datetime | 更新时间 |
**2. 权限组-部门关联表 `sys_permission_group_department`**
| 字段 | 类型 | 说明 |
|------|------|------|
|
`id`
| bigint | 主键,自增 |
|
`group_id`
| bigint | 权限组ID |
|
`department_id`
| bigint | 部门ID |
|
`create_time`
| datetime | 创建时间 |
**索引**
:
`idx_group_id(group_id)`
、
`idx_department_id(department_id)`
**3. 权限组-角色关联表 `sys_permission_group_role`**
| 字段 | 类型 | 说明 |
|------|------|------|
|
`id`
| bigint | 主键,自增 |
|
`group_id`
| bigint | 权限组ID |
|
`role_id`
| bigint | 角色ID |
|
`create_time`
| datetime | 创建时间 |
**索引**
:
`idx_group_id(group_id)`
、
`idx_role_id(role_id)`
**4. 权限组-用户关联表 `sys_permission_group_user`**
| 字段 | 类型 | 说明 |
|------|------|------|
|
`id`
| bigint | 主键,自增 |
|
`group_id`
| bigint | 权限组ID |
|
`user_id`
| bigint | 用户ID |
|
`create_time`
| datetime | 创建时间 |
**索引**
:
`idx_group_id(group_id)`
、
`idx_user_id(user_id)`
**3. permission.json 配置文件格式**
```
json
{
"permission1"
:
{
"create"
:
0
,
"delete"
:
0
,
"edit"
:
0
},
"permission2"
:
{
"create"
:
0
,
"delete"
:
0
,
"edit"
:
0
,
"view"
:
0
}
}
```
### 接口设计
| 接口 | URL | 方法 | 说明 |
|------|-----|------|------|
| 分页查询权限组 |
`/permissionGroup/getPage`
| POST | 返回权限组分页列表 |
| 获取所有权限配置 |
`/permissionGroup/getAllPermissions`
| GET | 读取permission.json返回所有可选权限 |
| 新增权限组 |
`/permissionGroup/add`
| POST | 创建权限组及权限配置 |
| 修改权限组 |
`/permissionGroup/update`
| PUT | 更新权限组名称和权限配置 |
| 删除权限组 |
`/permissionGroup/delete`
| POST | 物理删除权限组,级联删除关联记录 |
| 绑定关系 |
`/permissionGroup/bindRelation`
| POST | 绑定部门/角色/用户(type区分) |
| 解绑关系 |
`/permissionGroup/unbindRelation`
| POST | 解绑部门/角色/用户 |
| 查询绑定关系 |
`/permissionGroup/getRelations`
| POST | 查询权限组已绑定的对象列表 |
| 查询用户最终权限 |
`/permissionGroup/getUserPermissions`
| GET | 返回用户通过所有途径获得的并集权限 |
### 权限合并逻辑
查询用户最终权限时,合并以下三个来源的权限组:
1.
**直接绑定**
:查询
`sys_permission_group_user`
表中
`user_id=用户ID`
的权限组
2.
**部门绑定**
:查询
`sys_permission_group_department`
表中
`department_id=用户所属部门ID`
的权限组
3.
**角色绑定**
:查询
`sys_permission_group_role`
表中
`role_id=用户所属角色ID`
的权限组
合并策略:对所有权限组的
`permissions`
字段进行JSON并集,同一操作项有任意一个为1则最终为1。
### Steps
1.
在
[
ubains-meeting-persistence
](
ubains-meeting-persistence/src/main
)
创建
`PermissionGroup`
、
`PermissionGroupDepartment`
、
`PermissionGroupRole`
、
`PermissionGroupUser`
四个实体类,Mapper接口继承
`BaseMapper`
2.
在
[
ubains-meeting-provider
](
ubains-meeting-provider/src/main
)
创建
`IPermissionGroupService`
接口及实现类,实现CRUD、绑定解绑、权限并集合并逻辑
3.
在
[
ubains-meeting-inner-api/resources
](
ubains-meeting-inner-api/src/main/resources
)
创建
`permission.json`
配置文件
4.
在
[
ubains-meeting-inner-api
](
ubains-meeting-inner-api/src/main
)
创建
`PermissionGroupController`
,实现9个API接口,删除接口使用
`@Transactional`
保证级联删除的事务一致性
5.
创建
`PermissionUtil`
工具类封装权限JSON并集合并方法,供Service层和权限校验拦截器调用
6.
在src/main/java/com/ubains/meeting/common/AdjustmentDb.java的adjustmentDb方法创建对应数据库表
7.
输出PermissionGroupController的接口文档到Docs/Doc/权限管理目录下“API_权限组管理接口文档.md”
### SQL参考
```
sql
-- 权限组表
CREATE
TABLE
`sys_permission_group`
(
`group_id`
bigint
NOT
NULL
AUTO_INCREMENT
,
`group_name`
varchar
(
100
)
NOT
NULL
COMMENT
'权限组名称'
,
`permissions`
text
COMMENT
'权限配置JSON'
,
`company_number`
varchar
(
64
)
DEFAULT
NULL
COMMENT
'公司编号'
,
`is_enable`
int
DEFAULT
1
COMMENT
'是否启用(0:禁用,1:启用)'
,
`is_base`
int
DEFAULT
0
COMMENT
'是否基础权限组(0:否,1:是)'
,
`create_time`
datetime
DEFAULT
CURRENT_TIMESTAMP
,
`update_time`
datetime
DEFAULT
CURRENT_TIMESTAMP
ON
UPDATE
CURRENT_TIMESTAMP
,
PRIMARY
KEY
(
`group_id`
),
KEY
`idx_company`
(
`company_number`
)
)
ENGINE
=
InnoDB
DEFAULT
CHARSET
=
utf8mb4
COMMENT
=
'权限组表'
;
-- 权限组-部门关联表
CREATE
TABLE
`sys_permission_group_department`
(
`id`
bigint
NOT
NULL
AUTO_INCREMENT
,
`group_id`
bigint
NOT
NULL
COMMENT
'权限组ID'
,
`department_id`
bigint
NOT
NULL
COMMENT
'部门ID'
,
`create_time`
datetime
DEFAULT
CURRENT_TIMESTAMP
,
PRIMARY
KEY
(
`id`
),
KEY
`idx_group_id`
(
`group_id`
),
KEY
`idx_department_id`
(
`department_id`
)
)
ENGINE
=
InnoDB
DEFAULT
CHARSET
=
utf8mb4
COMMENT
=
'权限组-部门关联表'
;
-- 权限组-角色关联表
CREATE
TABLE
`sys_permission_group_role`
(
`id`
bigint
NOT
NULL
AUTO_INCREMENT
,
`group_id`
bigint
NOT
NULL
COMMENT
'权限组ID'
,
`role_id`
bigint
NOT
NULL
COMMENT
'角色ID'
,
`create_time`
datetime
DEFAULT
CURRENT_TIMESTAMP
,
PRIMARY
KEY
(
`id`
),
KEY
`idx_group_id`
(
`group_id`
),
KEY
`idx_role_id`
(
`role_id`
)
)
ENGINE
=
InnoDB
DEFAULT
CHARSET
=
utf8mb4
COMMENT
=
'权限组-角色关联表'
;
-- 权限组-用户关联表
CREATE
TABLE
`sys_permission_group_user`
(
`id`
bigint
NOT
NULL
AUTO_INCREMENT
,
`group_id`
bigint
NOT
NULL
COMMENT
'权限组ID'
,
`user_id`
bigint
NOT
NULL
COMMENT
'用户ID'
,
`create_time`
datetime
DEFAULT
CURRENT_TIMESTAMP
,
PRIMARY
KEY
(
`id`
),
KEY
`idx_group_id`
(
`group_id`
),
KEY
`idx_user_id`
(
`user_id`
)
)
ENGINE
=
InnoDB
DEFAULT
CHARSET
=
utf8mb4
COMMENT
=
'权限组-用户关联表'
;
```
### 其他
1.
把AI的总结内容输出ubains-meeting-parent
\D
ocs
\D
oc
\权
限管理目录下的“PRD_权限组管理_增删改查_权限组功能开发设计的总结.md”
2.
日志和注释要详细,尤其是日志,详细到每个步骤和返回,采用LogsUtil的logxxxInverted方法
\ No newline at end of file
AuxiliaryTool/AIPerfectedTestCases/config/开发PRD/PRD_权限组管理_权限结构_调整权限数据结构.md
0 → 100644
浏览文件 @
ed7ea006
# 权限组管理_权限结构_调整权限数据结构
## 概述
原开发的设计文档:Docs
\P
RD
\P
RD_系统
\权
限管理
\P
RD_权限组管理_增删改查_权限组功能开发设计.md
代码:com.ubains.controller.system.PermissionGroupController
## 目标
1.
调整读取permission.json权限配置文件(getAllPermissions)和获取用户最终权限方法(getUserPermissions),
原permission.json的数据结构为
`{"permission1":{"create":1,"delete":0}}`
调整后的数据结构为
`{"class1":{"permission1":{"create":0,"delete":0,"edit":0}},"class2":{"permission1":{"create":0,"delete":0,"edit":0}}}`
,原来的基础上增加了一层权限分类。
2.
getAllPermissions方法返回的数据要求permission.json的数据顺序一致。
### 其他
1.
把AI的总结内容输出ubains-meeting-parent
\D
ocs
\D
oc
\权
限管理目录下的“PRD_权限组管理_权限结构_调整权限数据结构的总结.md”
2.
日志和注释要详细,尤其是日志,详细到每个步骤和返回,采用LogsUtil的logxxxInverted方法
\ No newline at end of file
AuxiliaryTool/AIPerfectedTestCases/config/开发PRD/PRD_权限组管理_添加权限组_给基础角色添加基本权限组.md
0 → 100644
浏览文件 @
ed7ea006
# 需求文档
## 概述
给基础角色添加基本权限组。
基础角色:
-
`超级管理员`
角色id = 1
-
`系统管理员`
角色id = 6
-
`安全管理员`
角色id = 7
-
`审计管理员`
角色id = 8
相关文件和代码:
-
com.ubains.meeting.common.AdjustmentDb.adjustmentDb 数据库迁移方法,系统初始化时执行
-
com.ubains.meeting.system.entity.Role 角色实体类
-
com.ubains.meeting.system.entity.PermissionGroup 权限组实体类
-
-
权限组设计相关文件:
-
Docs/PRD/PRD_系统/权限管理 该目录下的其他文档是原来权限组设计的文档
-
ubains-meeting-inner-api/src/main/resources/config/permission.yml 权限文件
### 任务
-
给基础角色添加基本权限组,权限组名称对应角色名称
-
**权限组按公司编号隔离**
:每个公司都需要创建4个基础权限组,通过
`company_number`
字段区分
-
在
`PermissionGroupService`
中实现初始化逻辑,在
`adjustmentDb`
方法中调用
-
超级管理员读取
`permission.yml`
文件获取所有权限,作为权限组的权限,关联角色id为1
-
系统管理员读取
`permission-system.yml`
文件(需创建)获取所有权限,作为权限组的权限,关联角色id为6
-
安全管理员读取
`permission-security.yml`
文件(需创建)获取所有权限,作为权限组的权限,关联角色id为7
-
审计管理员读取
`permission-audit.yml`
文件(需创建)获取所有权限,作为权限组的权限,关联角色id为8
-
基础权限组的
`isBase`
字段值为1
-
**ID说明**
:
`role_id`
固定为1/6/7/8,
`group_id`
和关联表
`id`
为自增主键,通过
`is_base`
+
`group_name`
+
`company_number`
判断权限组是否存在
-
**优化 `PermissionUtil#loadPermissionConfig()` 方法**
:新增带参数的重载方法
`loadPermissionConfig(String fileName)`
,支持读取指定的权限配置文件(如
`permission-system.yml`
)
-
开发前先给出计划执行方案文档到在
`Docs/PRD/PRD_系统/权限管理/同名文件+_计划执行.md`
中
-
生成的计划执行方案文档需添加任务:生成ai输出的总结内容到
`Docs/Doc/权限管理/Doc_+同名文件(不包含前缀PRD_)+的总结.md`
### 方法总结
-
Docs/PRD/PRD模板、规范/_PRD_方法总结_记录文档.md
### 代码规范
-
Docs/PRD/PRD模板、规范/_PRD_规范文档_代码规范.md
### 文档规范
-
Docs/PRD/PRD模板、规范/_PRD_规范文档_文档规范.md
### 测试规范
-
Docs/PRD/PRD模板、规范/_PRD_规范文档_测试规范.md
### 记录文档
-
Docs/PRD/PRD模板、规范/_PRD_问题总结_记录文档.md
AuxiliaryTool/AIPerfectedTestCases/config/开发PRD/PRD_权限组管理_详情查看_添加权限组详情查看.md
0 → 100644
浏览文件 @
ed7ea006
# 需求文档
## 概述
权限组目前需要查看详情功能,详情除了基本权限组信息外,还需要有已绑定的角色、部门和用户信息。
**权限组的控制层**
:com.ubains.controller.system.PermissionGroupController
## 任务
-
在
`权限组的控制层`
上添加新的接口,该接口用于查看权限组详情,该详情包含权限组基本信息、已绑定的角色、部门、用户信息
-
角色、部门、用户信息,不需要多余信息,主要显示名称和id即可
-
注意接口返回要结果加密,使用
`@EncryptResult`
注解
-
先给出计划执行方案文档,将文档创建在
`Docs/PRD/系统管理/权限管理/同名文件+_计划执行.md`
,等审批完文档再由我考虑是否按计划执行文档来执行
-
生成的计划执行方案文档需添加任务:生成API文档到
`Docs/Doc/系统管理/权限管理/Doc_+同名文件(不包含前缀PRD_)+_API.md`
-
生成的计划执行方案文档需添加任务:生成ai输出的总结内容到
`Docs/Doc/系统管理/权限管理/Doc_+同名文件(不包含前缀PRD_)+的总结.md`
## 规范
严格执行以下文档
#### 代码规范
-
Docs/PRD/_PRD_规范文档_代码规范.md
#### 文档规范
-
Docs/PRD/_PRD_规范文档_文档规范.md
#### 测试规范
-
Docs/PRD/_PRD_规范文档_测试规范.md
\ No newline at end of file
AuxiliaryTool/AIPerfectedTestCases/config/测试用例/新统一平台权限管理测试用例.xlsx
0 → 100644
浏览文件 @
ed7ea006
File added
AuxiliaryTool/AIPerfectedTestCases/requirements.txt
0 → 100644
浏览文件 @
ed7ea006
# AI完善测试用例工具 - 依赖包列表
# Anthropic AI SDK - Claude API
anthropic>=0.18.0
# Excel文件处理
openpyxl>=3.1.0
# Word文档处理
python-docx>=1.1.0
# 数据处理(可选,用于高级分析)
pandas>=2.0.0
# 环境变量管理
python-dotenv>=1.0.0
# 其他依赖
python-dateutil>=2.8.0
AuxiliaryTool/AIPerfectedTestCases/run.py
0 → 100644
浏览文件 @
ed7ea006
# -*- coding: utf-8 -*-
"""
AI完善测试用例工具 - 入口脚本
使用方法:
python run.py # 使用默认配置
python run.py --help # 显示帮助信息
python run.py --log-level DEBUG # 设置日志级别
"""
import
sys
import
os
import
argparse
from
pathlib
import
Path
# 添加项目根目录到Python路径
PROJECT_ROOT
=
Path
(
__file__
)
.
parent
sys
.
path
.
insert
(
0
,
str
(
PROJECT_ROOT
))
# 加载环境变量
try
:
from
dotenv
import
load_dotenv
env_file
=
PROJECT_ROOT
/
".env"
if
env_file
.
exists
():
load_dotenv
(
env_file
)
# 已自动加载 .env 文件
except
ImportError
:
pass
# python-dotenv 未安装,跳过
from
src.config
import
(
REQUIREMENTS_DIR
,
PRD_DIR
,
TEST_CASE_FILE
,
OUTPUT_TEST_CASE
,
OUTPUT_REPORT
,
API_KEY_ENV
,
get_project_info
,
AI_MODEL
)
def
print_banner
()
->
None
:
"""打印欢迎横幅"""
print
(
"="
*
60
)
print
(
" AI完善测试用例工具 v1.0.0"
)
print
(
"="
*
60
)
print
()
def
print_project_info
()
->
None
:
"""打印项目信息"""
info
=
get_project_info
()
print
(
"【项目配置】"
)
print
(
f
" 项目根目录: {info['project_root']}"
)
print
(
f
" 需求文档目录: {info['requirements_dir']}"
)
print
(
f
" PRD文档目录: {info['prd_dir']}"
)
print
(
f
" 测试用例文件: {info['test_case_file']}"
)
print
(
f
" AI模型: {info['ai_model']}"
)
print
(
f
" 覆盖率目标: {info['coverage_target'] * 100}
%
"
)
print
()
def
check_environment
()
->
bool
:
"""
检查运行环境
Returns:
环境是否正常
"""
print
(
"【环境检查】"
)
# 检查API密钥
api_key
=
os
.
environ
.
get
(
API_KEY_ENV
)
if
api_key
:
masked_key
=
api_key
[:
8
]
+
"..."
if
len
(
api_key
)
>
8
else
"***"
print
(
f
" ✓ API密钥: {masked_key}"
)
else
:
print
(
f
" ✗ API密钥: 未设置 ({API_KEY_ENV})"
)
print
()
print
(
"请设置API密钥:"
)
print
(
f
" Windows: set {API_KEY_ENV}=your-key-here"
)
print
(
f
" Linux/Mac: export {API_KEY_ENV}=your-key-here"
)
return
False
# 检查需求文档目录
if
REQUIREMENTS_DIR
.
exists
():
req_files
=
list
(
REQUIREMENTS_DIR
.
glob
(
"*.docx"
))
print
(
f
" ✓ 需求文档: {len(req_files)} 份"
)
else
:
print
(
f
" ✗ 需求文档目录不存在: {REQUIREMENTS_DIR}"
)
return
False
# 检查PRD文档目录
if
PRD_DIR
.
exists
():
prd_files
=
list
(
PRD_DIR
.
glob
(
"*.md"
))
print
(
f
" ✓ PRD文档: {len(prd_files)} 份"
)
else
:
print
(
f
" ✗ PRD文档目录不存在: {PRD_DIR}"
)
return
False
# 检查测试用例文件
if
TEST_CASE_FILE
.
exists
():
print
(
f
" ✓ 测试用例文件: {TEST_CASE_FILE.name}"
)
else
:
print
(
f
" ✗ 测试用例文件不存在: {TEST_CASE_FILE}"
)
return
False
print
()
return
True
def
test_api_connection
()
->
int
:
"""
测试API连接
Returns:
退出码(0表示成功,非0表示失败)
"""
print
(
"【测试API连接】"
)
print
()
api_key
=
os
.
environ
.
get
(
API_KEY_ENV
)
if
not
api_key
:
print
(
"✗ 未设置API密钥"
)
print
(
f
" 请设置环境变量: {API_KEY_ENV}"
)
print
(
" 或在 .env 文件中配置"
)
return
1
# 隐藏密钥显示
masked_key
=
api_key
[:
8
]
+
"..."
if
len
(
api_key
)
>
8
else
"***"
print
(
f
"API密钥: {masked_key}"
)
print
(
f
"AI模型: {AI_MODEL}"
)
print
()
try
:
import
anthropic
client
=
anthropic
.
Anthropic
(
api_key
=
api_key
)
print
(
"发送测试请求..."
)
response
=
client
.
messages
.
create
(
model
=
AI_MODEL
,
max_tokens
=
50
,
messages
=
[{
"role"
:
"user"
,
"content"
:
"请回复:API连接成功"
}]
)
result
=
response
.
content
[
0
]
.
text
print
()
print
(
"✓ API连接正常"
)
print
(
f
" AI回复: {result}"
)
return
0
except
anthropic
.
AuthenticationError
:
print
()
print
(
"✗ API密钥认证失败"
)
print
(
" 请检查API密钥是否正确"
)
return
1
except
anthropic
.
PermissionDeniedError
:
print
()
print
(
"✗ API权限不足"
)
print
(
" 请检查API密钥是否有调用权限"
)
return
1
except
anthropic
.
RateLimitError
:
print
()
print
(
"✗ API调用频率超限"
)
print
(
" 请稍后再试"
)
return
1
except
anthropic
.
APIError
as
e
:
print
()
print
(
f
"✗ API错误: {e}"
)
return
1
except
Exception
as
e
:
print
()
print
(
f
"✗ 连接失败: {e}"
)
return
1
def
parse_arguments
()
->
argparse
.
Namespace
:
"""
解析命令行参数
Returns:
解析后的参数对象
"""
parser
=
argparse
.
ArgumentParser
(
description
=
"AI完善测试用例工具 - 通过AI分析需求文档和PRD文档,自动完善测试用例"
,
formatter_class
=
argparse
.
RawDescriptionHelpFormatter
,
epilog
=
"""
示例用法:
python run.py # 使用默认配置运行
python run.py --log-level DEBUG # 启用详细日志
python run.py --show-info # 显示项目信息
python run.py --mode api # 强制使用API模式
python run.py --mode cli # 强制使用CLI模式
python run.py --api-key sk-ant-... # 指定API密钥
环境变量:
ANTHROPIC_API_KEY # Anthropic API密钥(API模式必需)
AI_SERVICE_MODE # AI服务模式:auto/api/cli
输出文件:
完善后的测试用例: Perfected/新统一平台权限管理测试用例_完善版本.xlsx
差异性报告: reports/新统一平台权限管理测试用例_差异性报告.docx
"""
)
parser
.
add_argument
(
'--log-level'
,
type
=
str
,
dest
=
'log_level'
,
default
=
'INFO'
,
choices
=
[
'DEBUG'
,
'INFO'
,
'WARNING'
,
'ERROR'
],
help
=
'日志级别(默认为INFO)'
)
parser
.
add_argument
(
'--show-info'
,
action
=
'store_true'
,
dest
=
'show_info'
,
help
=
'显示项目信息后退出'
)
parser
.
add_argument
(
'--check-only'
,
action
=
'store_true'
,
dest
=
'check_only'
,
help
=
'仅检查环境,不执行处理'
)
parser
.
add_argument
(
'--test-api'
,
action
=
'store_true'
,
dest
=
'test_api'
,
help
=
'测试API连接'
)
parser
.
add_argument
(
'--use-cli'
,
action
=
'store_true'
,
dest
=
'use_cli'
,
help
=
'强制使用Claude CLI模式(已弃用,建议使用--mode cli)'
)
parser
.
add_argument
(
'--mode'
,
type
=
str
,
dest
=
'mode'
,
default
=
None
,
choices
=
[
'auto'
,
'api'
,
'cli'
],
help
=
'AI服务模式:auto(自动检测)、api(Anthropic API)、cli(Claude CLI)'
)
parser
.
add_argument
(
'--api-key'
,
type
=
str
,
dest
=
'api_key'
,
default
=
None
,
help
=
'Anthropic API密钥(覆盖环境变量)'
)
return
parser
.
parse_args
()
def
run_cli
(
args
:
argparse
.
Namespace
)
->
int
:
"""
运行命令行程序
Args:
args: 命令行参数
Returns:
退出码(0表示成功,非0表示失败)
"""
# 设置AI服务模式(通过环境变量传递)
if
args
.
mode
:
os
.
environ
[
"AI_SERVICE_MODE"
]
=
args
.
mode
# 设置API密钥(通过环境变量传递)
if
args
.
api_key
:
os
.
environ
[
API_KEY_ENV
]
=
args
.
api_key
# 测试API连接
if
args
.
test_api
:
return
test_api_connection
()
# 打印横幅
print_banner
()
# 仅显示信息
if
args
.
show_info
:
print_project_info
()
return
0
# 仅检查环境
if
args
.
check_only
:
if
check_environment
():
print
(
"✓ 环境检查通过"
)
return
0
else
:
print
(
"✗ 环境检查失败"
)
return
1
# 检查环境
if
not
check_environment
():
return
1
# 打印项目信息
print_project_info
()
# 导入主模块并运行
from
src.main
import
main
as
run_main
print
(
"【开始处理】"
)
print
()
return
run_main
(
use_cli
=
args
.
use_cli
)
def
main
()
->
int
:
"""
主入口函数
Returns:
退出码
"""
args
=
parse_arguments
()
return
run_cli
(
args
)
if
__name__
==
"__main__"
:
try
:
exit_code
=
main
()
sys
.
exit
(
exit_code
)
except
KeyboardInterrupt
:
print
(
"
\n\n
操作已取消"
)
sys
.
exit
(
130
)
except
Exception
as
e
:
print
(
f
"
\n
错误: {e}"
)
import
traceback
traceback
.
print_exc
()
sys
.
exit
(
1
)
AuxiliaryTool/AIPerfectedTestCases/src/__init__.py
0 → 100644
浏览文件 @
ed7ea006
# -*- coding: utf-8 -*-
"""
AI完善测试用例工具
本工具通过结合已有测试用例、需求文档、开发需求PRD文档,
利用AI能力对测试用例进行查漏补缺,完善测试用例内容。
"""
__version__
=
"1.0.0"
__author__
=
"AI Test Case Enhancer"
AuxiliaryTool/AIPerfectedTestCases/src/ai_service_manager.py
0 → 100644
浏览文件 @
ed7ea006
# -*- coding: utf-8 -*-
"""
AI服务管理器模块
统一管理Anthropic API和Claude CLI两种调用方式
"""
import
os
import
subprocess
from
typing
import
Optional
,
List
,
Dict
,
Any
from
logging
import
getLogger
from
src.config
import
API_KEY_ENV
,
API_KEY_PREFIX
,
AI_MODEL
,
get_api_base_url
logger
=
getLogger
(
__name__
)
class
AIServiceManager
:
"""
AI服务管理器
自动选择并管理AI服务(API或CLI)
"""
def
__init__
(
self
,
api_key
:
Optional
[
str
]
=
None
,
use_cli
:
bool
=
False
):
"""
初始化AI服务管理器
Args:
api_key: Anthropic API密钥(可选)
use_cli: 是否强制使用CLI模式
"""
self
.
mode
=
self
.
_determine_mode
(
api_key
,
use_cli
)
self
.
cli_adapter
=
None
self
.
api_client
=
None
if
self
.
mode
==
"cli"
:
self
.
_init_cli_mode
()
elif
self
.
mode
==
"api"
:
self
.
_init_api_mode
(
api_key
)
logger
.
info
(
"AI服务管理器初始化完成,模式:
%
s"
,
self
.
mode
)
def
_determine_mode
(
self
,
api_key
:
Optional
[
str
],
use_cli
:
bool
)
->
str
:
"""确定使用哪种模式"""
# 检查CLI是否可用
cli_available
=
self
.
_check_cli_available
()
if
use_cli
:
if
not
cli_available
:
raise
RuntimeError
(
"强制使用CLI模式,但Claude CLI不可用"
)
return
"cli"
if
api_key
and
api_key
.
strip
():
return
"api"
# 自动检测
api_key_from_env
=
os
.
environ
.
get
(
API_KEY_ENV
,
""
)
if
api_key_from_env
and
api_key_from_env
.
strip
():
return
"api"
# 回退到CLI模式(如果可用)
if
cli_available
:
return
"cli"
# 都不可用,抛出异常
raise
RuntimeError
(
"未找到可用的AI服务。请配置以下之一:
\n
"
f
" 1. 设置环境变量 {API_KEY_ENV}
\n
"
" 2. 安装Claude CLI: npm install -g @anthropic-ai/claude-code"
)
def
_check_cli_available
(
self
)
->
bool
:
"""检查Claude CLI是否可用"""
try
:
result
=
subprocess
.
run
(
[
"claude"
,
"--version"
],
capture_output
=
True
,
timeout
=
5
)
return
result
.
returncode
==
0
except
(
FileNotFoundError
,
subprocess
.
TimeoutExpired
):
return
False
def
_init_cli_mode
(
self
):
"""初始化CLI模式"""
try
:
from
src.claude_cli_adapter
import
ClaudeCLIAdapter
self
.
cli_adapter
=
ClaudeCLIAdapter
()
if
not
self
.
cli_adapter
.
check_available
():
raise
RuntimeError
(
"Claude CLI不可用"
)
logger
.
info
(
"Claude CLI模式已启用"
)
except
ImportError
:
raise
RuntimeError
(
"无法导入Claude CLI适配器"
)
def
_init_api_mode
(
self
,
api_key
:
str
):
"""初始化API模式"""
try
:
import
anthropic
base_url
=
get_api_base_url
()
kwargs
=
{
"api_key"
:
api_key
}
if
base_url
!=
"https://api.anthropic.com"
:
kwargs
[
"base_url"
]
=
base_url
logger
.
info
(
"使用自定义API endpoint:
%
s"
,
base_url
)
self
.
api_client
=
anthropic
.
Anthropic
(
**
kwargs
)
logger
.
info
(
"Anthropic API模式已启用"
)
except
ImportError
:
raise
RuntimeError
(
"未安装anthropic库,请运行: pip install anthropic"
)
def
ask_json
(
self
,
prompt
:
str
,
model
:
Optional
[
str
]
=
None
)
->
Any
:
"""
发送提示词并返回JSON结果
Args:
prompt: 提示词
model: 模型名称(可选)
Returns:
解析后的JSON对象
"""
model
=
model
or
AI_MODEL
if
self
.
mode
==
"cli"
:
# CLI模式:适配器会自动处理JSON解析
return
self
.
cli_adapter
.
ask_json
(
prompt
,
model
)
else
:
# API模式:需要手动调用API并解析
response
=
self
.
api_client
.
messages
.
create
(
model
=
model
,
max_tokens
=
8192
,
temperature
=
0.3
,
messages
=
[{
"role"
:
"user"
,
"content"
:
prompt
}]
)
result_text
=
response
.
content
[
0
]
.
text
return
self
.
_parse_json
(
result_text
)
def
ask_json_array
(
self
,
prompt
:
str
,
model
:
Optional
[
str
]
=
None
)
->
List
[
Dict
]:
"""
发送提示词并返回JSON数组结果
Args:
prompt: 提示词
model: 模型名称(可选)
Returns:
解析后的JSON数组
"""
model
=
model
or
AI_MODEL
if
self
.
mode
==
"cli"
:
# CLI模式:适配器会自动处理JSON数组解析
return
self
.
cli_adapter
.
ask_json_array
(
prompt
,
model
)
else
:
# API模式:需要手动调用API并解析
response
=
self
.
api_client
.
messages
.
create
(
model
=
model
,
max_tokens
=
8192
,
temperature
=
0.3
,
messages
=
[{
"role"
:
"user"
,
"content"
:
prompt
}]
)
result_text
=
response
.
content
[
0
]
.
text
return
self
.
_parse_json_array
(
result_text
)
def
_extract_json
(
self
,
text
:
str
)
->
str
:
"""
从文本中提取JSON字符串
支持多种格式:
1. 纯JSON
2. 包含额外文本
3. 代码块格式
4. 多个JSON对象(取第一个)
Args:
text: 包含JSON的文本
Returns:
提取的JSON字符串,空字符串表示未找到
"""
import
re
import
json
text
=
text
.
strip
()
# 尝试直接解析
try
:
json
.
loads
(
text
)
return
text
except
:
pass
# 尝试从代码块中提取
code_block_pattern
=
r'```(?:json)?\s*\n?([\s\S]*?)\n?```'
matches
=
re
.
findall
(
code_block_pattern
,
text
)
for
match
in
matches
:
try
:
json
.
loads
(
match
.
strip
())
return
match
.
strip
()
except
:
continue
# 尝试提取花括号内容(对象)- 使用正则找到第一个完整对象
# 使用括号匹配来处理嵌套
brace_count
=
0
start_idx
=
-
1
for
i
,
char
in
enumerate
(
text
):
if
char
==
'{'
:
if
brace_count
==
0
:
start_idx
=
i
brace_count
+=
1
elif
char
==
'}'
:
brace_count
-=
1
if
brace_count
==
0
and
start_idx
>=
0
:
try
:
json_str
=
text
[
start_idx
:
i
+
1
]
json
.
loads
(
json_str
)
return
json_str
except
:
break
# 尝试提取方括号内容(数组)
bracket_count
=
0
start_idx
=
-
1
for
i
,
char
in
enumerate
(
text
):
if
char
==
'['
:
if
bracket_count
==
0
:
start_idx
=
i
bracket_count
+=
1
elif
char
==
']'
:
bracket_count
-=
1
if
bracket_count
==
0
and
start_idx
>=
0
:
try
:
json_str
=
text
[
start_idx
:
i
+
1
]
json
.
loads
(
json_str
)
return
json_str
except
:
break
return
""
def
_parse_json
(
self
,
text
:
str
)
->
Dict
:
"""解析JSON文本"""
import
json
logger
.
debug
(
"尝试解析JSON,内容长度:
%
d"
,
len
(
text
))
# 使用增强的提取方法
json_str
=
self
.
_extract_json
(
text
)
if
json_str
:
try
:
result
=
json
.
loads
(
json_str
)
# 如果返回的是数组,取第一个元素(兼容智谱AI等返回格式)
if
isinstance
(
result
,
list
):
if
len
(
result
)
>
0
:
if
isinstance
(
result
[
0
],
dict
):
logger
.
info
(
"JSON解析结果是数组(
%
d个元素),取第一个元素"
,
len
(
result
))
return
result
[
0
]
else
:
logger
.
warning
(
"JSON数组第一个元素不是对象类型:
%
s"
,
type
(
result
[
0
]))
else
:
logger
.
warning
(
"JSON数组为空"
)
raise
ValueError
(
"JSON解析结果为空数组或格式不正确"
)
return
result
except
json
.
JSONDecodeError
as
e
:
logger
.
error
(
"JSON解析失败:
%
s"
,
e
)
logger
.
debug
(
"提取的JSON字符串:
%
s"
,
json_str
[:
500
])
raise
ValueError
(
f
"无法解析JSON: {text[:200]}"
)
def
_parse_json_array
(
self
,
text
:
str
)
->
List
:
"""解析JSON数组文本"""
import
json
logger
.
debug
(
"尝试解析JSON数组,内容长度:
%
d"
,
len
(
text
))
# 使用增强的提取方法
json_str
=
self
.
_extract_json
(
text
)
if
json_str
:
try
:
result
=
json
.
loads
(
json_str
)
if
isinstance
(
result
,
list
):
return
result
else
:
# 如果是对象,尝试提取数组字段
if
isinstance
(
result
,
dict
):
# 检查是否是标准的gap_detector返回格式(包含三个键)
gap_keys
=
{
"功能点缺失"
,
"场景覆盖不足"
,
"边界条件不足"
}
if
gap_keys
.
issubset
(
result
.
keys
()):
# 这是gap_detector的完整返回格式,直接返回整个对象
logger
.
info
(
"识别到完整的gap检测结果格式"
)
return
result
# 否则返回第一个找到的数组(兼容旧逻辑)
for
key
,
value
in
result
.
items
():
if
isinstance
(
value
,
list
):
logger
.
info
(
"从JSON对象中提取数组:
%
s"
,
key
)
return
value
return
[
result
]
except
json
.
JSONDecodeError
as
e
:
logger
.
error
(
"JSON数组解析失败:
%
s"
,
e
)
logger
.
debug
(
"提取的JSON字符串:
%
s"
,
json_str
[:
500
])
# 如果解析失败,记录原始文本以便调试
logger
.
warning
(
"JSON数组解析失败,记录原始内容(前1000字符):"
)
logger
.
warning
(
text
[:
1000
])
raise
ValueError
(
f
"无法解析JSON数组: {text[:200]}"
)
@
staticmethod
def
create
(
auto_select
:
bool
=
True
)
->
'AIServiceManager'
:
"""
工厂方法:创建AI服务管理器
Args:
auto_select: 是否自动选择模式(默认True)
Returns:
AI服务管理器实例
"""
if
auto_select
:
return
AIServiceManager
()
else
:
# 使用API模式
api_key
=
os
.
environ
.
get
(
API_KEY_ENV
)
if
not
api_key
or
not
api_key
.
strip
():
raise
ValueError
(
"未配置API密钥"
)
return
AIServiceManager
(
api_key
=
api_key
)
AuxiliaryTool/AIPerfectedTestCases/src/case_generator.py
0 → 100644
浏览文件 @
ed7ea006
# -*- coding: utf-8 -*-
"""
用例生成模块
本模块负责使用AI生成缺失的测试用例
"""
from
typing
import
List
,
Dict
,
Optional
from
logging
import
getLogger
import
json
import
re
from
datetime
import
datetime
from
src.config
import
(
AI_MODEL
,
AI_MAX_TOKENS
,
AI_TEMPERATURE
,
TEST_CASE_COLUMNS
,
CASE_LEVELS
,
DEFAULT_CASE_LEVEL
,
DEFAULT_TEST_FREQUENCY
,
TEST_FREQUENCIES
)
logger
=
getLogger
(
__name__
)
class
CaseGenerator
:
"""
测试用例生成器
主要功能:
1. 为功能点缺失生成测试用例
2. 为场景覆盖不足生成测试用例
3. 为边界条件不足生成测试用例
支持两种AI服务模式:
- Anthropic API:使用官方API密钥
- Claude CLI:使用本地CLI工具
"""
def
__init__
(
self
,
api_key
:
Optional
[
str
]
=
None
,
use_cli
:
bool
=
False
):
"""
初始化生成器
Args:
api_key: Anthropic API密钥(可选,兼容性保留)
use_cli: 是否使用CLI模式
"""
from
src.ai_service_manager
import
AIServiceManager
# 使用AI服务管理器
self
.
ai_service
=
AIServiceManager
(
api_key
=
api_key
,
use_cli
=
use_cli
)
self
.
case_counter
=
1
# 用于生成序列号
logger
.
info
(
"测试用例生成器初始化完成"
)
def
generate_cases
(
self
,
gaps
:
Dict
,
requirements
:
List
[
Dict
],
prds
:
List
[
Dict
],
existing_cases
:
List
[
Dict
])
->
List
[
Dict
]:
"""
生成缺失的测试用例
Args:
gaps: 缺失检测结果
requirements: 需求文档列表
prds: PRD文档列表
existing_cases: 现有测试用例列表
Returns:
新生成的测试用例列表
"""
logger
.
info
(
"开始生成测试用例..."
)
new_cases
=
[]
# 获取下一个序列号
next_seq
=
self
.
_get_next_sequence
(
existing_cases
)
# 为功能点缺失生成用例
function_cases
=
self
.
_generate_cases_for_functions
(
gaps
.
get
(
"功能点缺失"
,
[]),
requirements
,
prds
,
next_seq
)
new_cases
.
extend
(
function_cases
)
next_seq
+=
len
(
function_cases
)
# 为场景覆盖不足生成用例
scenario_cases
=
self
.
_generate_cases_for_scenarios
(
gaps
.
get
(
"场景覆盖不足"
,
[]),
requirements
,
prds
,
next_seq
)
new_cases
.
extend
(
scenario_cases
)
next_seq
+=
len
(
scenario_cases
)
# 为边界条件不足生成用例
boundary_cases
=
self
.
_generate_cases_for_boundaries
(
gaps
.
get
(
"边界条件不足"
,
[]),
requirements
,
prds
,
next_seq
)
new_cases
.
extend
(
boundary_cases
)
logger
.
info
(
"测试用例生成完成,共
%
d 条"
,
len
(
new_cases
))
return
new_cases
def
_get_next_sequence
(
self
,
existing_cases
:
List
[
Dict
])
->
int
:
"""
获取下一个序列号
Args:
existing_cases: 现有测试用例列表
Returns:
下一个序列号
"""
max_seq
=
0
for
case
in
existing_cases
:
seq
=
case
.
get
(
"序列号"
)
if
isinstance
(
seq
,
int
):
max_seq
=
max
(
max_seq
,
seq
)
elif
isinstance
(
seq
,
str
):
try
:
max_seq
=
max
(
max_seq
,
int
(
seq
))
except
ValueError
:
pass
return
max_seq
+
1
def
_generate_cases_for_functions
(
self
,
function_gaps
:
List
[
Dict
],
requirements
:
List
[
Dict
],
prds
:
List
[
Dict
],
start_seq
:
int
)
->
List
[
Dict
]:
"""
为功能点缺失生成测试用例
Args:
function_gaps: 功能点缺失列表
requirements: 需求文档列表
prds: PRD文档列表
start_seq: 起始序列号
Returns:
生成的测试用例列表
"""
logger
.
info
(
"为
%
d 个功能点缺失生成测试用例..."
,
len
(
function_gaps
))
cases
=
[]
for
i
,
gap
in
enumerate
(
function_gaps
):
try
:
case
=
self
.
_generate_case_for_function
(
gap
,
requirements
,
prds
,
start_seq
+
i
)
if
case
:
cases
.
append
(
case
)
except
Exception
as
e
:
logger
.
error
(
"生成功能点测试用例失败:
%
s, 错误:
%
s"
,
gap
.
get
(
"功能描述"
,
""
),
e
)
logger
.
info
(
"功能点测试用例生成完成:
%
d 条"
,
len
(
cases
))
return
cases
def
_generate_case_for_function
(
self
,
gap
:
Dict
,
requirements
:
List
[
Dict
],
prds
:
List
[
Dict
],
seq
:
int
)
->
Optional
[
Dict
]:
"""为单个功能点缺失生成测试用例"""
prompt
=
f
"""请为以下缺失的功能点生成完整的测试用例。
【功能信息】
- 功能模块:{gap.get('功能模块', '')}
- 功能编号:{gap.get('功能编号', '')}
- 功能描述:{gap.get('功能描述', '')}
【要求】
1. 生成详细的测试步骤
2. 预期结果要明确具体
3. 考虑正常流程和基本验证
请输出JSON格式的测试用例:
"""
prompt
+=
json
.
dumps
({
col
:
""
for
col
in
TEST_CASE_COLUMNS
},
ensure_ascii
=
False
)
prompt
+=
f
"""
注意:
1. 必须输出合法的JSON格式
2. 用例编号格式:TC-{gap.get('功能模块', 'TEST')[:4].upper()}-{seq:03d}
3. 用例等级:{gap.get('优先级', DEFAULT_CASE_LEVEL)}
4. STEP要包含详细的操作步骤,每步一行
5. JSON字段填写:暂不涉及自动化测试
6. 测试频次:{DEFAULT_TEST_FREQUENCY}
"""
return
self
.
_call_ai_for_case
(
prompt
,
gap
.
get
(
"功能描述"
,
""
))
def
_generate_cases_for_scenarios
(
self
,
scenario_gaps
:
List
[
Dict
],
requirements
:
List
[
Dict
],
prds
:
List
[
Dict
],
start_seq
:
int
)
->
List
[
Dict
]:
"""为场景覆盖不足生成测试用例"""
logger
.
info
(
"为
%
d 个场景覆盖不足生成测试用例..."
,
len
(
scenario_gaps
))
cases
=
[]
for
i
,
gap
in
enumerate
(
scenario_gaps
):
try
:
case
=
self
.
_generate_case_for_scenario
(
gap
,
requirements
,
prds
,
start_seq
+
i
)
if
case
:
cases
.
append
(
case
)
except
Exception
as
e
:
logger
.
error
(
"生成场景测试用例失败:
%
s, 错误:
%
s"
,
gap
.
get
(
"缺失场景"
,
""
),
e
)
logger
.
info
(
"场景测试用例生成完成:
%
d 条"
,
len
(
cases
))
return
cases
def
_generate_case_for_scenario
(
self
,
gap
:
Dict
,
requirements
:
List
[
Dict
],
prds
:
List
[
Dict
],
seq
:
int
)
->
Optional
[
Dict
]:
"""为单个场景覆盖不足生成测试用例"""
prompt
=
f
"""请为以下缺失的场景生成完整的测试用例。
【功能信息】
- 功能描述:{gap.get('功能描述', '')}
- 已有场景:{gap.get('已有场景', '')}
- 缺失场景:{gap.get('缺失场景', '')}
【要求】
1. 专门针对缺失场景设计测试步骤
2. 预期结果要明确描述该场景的预期行为
3. 考虑异常处理和边界情况
请输出JSON格式的测试用例:
"""
prompt
+=
json
.
dumps
({
col
:
""
for
col
in
TEST_CASE_COLUMNS
},
ensure_ascii
=
False
)
prompt
+=
f
"""
注意:
1. 必须输出合法的JSON格式
2. 用例名称:{gap.get('建议用例名称', f'测试{gap.get("功能描述", "")}{gap.get("缺失场景", "")}')}
3. 用例等级:{gap.get('优先级', DEFAULT_CASE_LEVEL)}
4. STEP要详细描述如何触发该场景
5. 预期结果要描述该场景的预期行为
"""
return
self
.
_call_ai_for_case
(
prompt
,
gap
.
get
(
"缺失场景"
,
""
))
def
_generate_cases_for_boundaries
(
self
,
boundary_gaps
:
List
[
Dict
],
requirements
:
List
[
Dict
],
prds
:
List
[
Dict
],
start_seq
:
int
)
->
List
[
Dict
]:
"""为边界条件不足生成测试用例"""
logger
.
info
(
"为
%
d 个边界条件不足生成测试用例..."
,
len
(
boundary_gaps
))
cases
=
[]
for
i
,
gap
in
enumerate
(
boundary_gaps
):
try
:
case
=
self
.
_generate_case_for_boundary
(
gap
,
requirements
,
prds
,
start_seq
+
i
)
if
case
:
cases
.
append
(
case
)
except
Exception
as
e
:
logger
.
error
(
"生成边界条件测试用例失败:
%
s, 错误:
%
s"
,
gap
.
get
(
"缺失边界"
,
""
),
e
)
logger
.
info
(
"边界条件测试用例生成完成:
%
d 条"
,
len
(
cases
))
return
cases
def
_generate_case_for_boundary
(
self
,
gap
:
Dict
,
requirements
:
List
[
Dict
],
prds
:
List
[
Dict
],
seq
:
int
)
->
Optional
[
Dict
]:
"""为单个边界条件不足生成测试用例"""
prompt
=
f
"""请为以下缺失的边界条件生成完整的测试用例。
【功能信息】
- 功能描述:{gap.get('功能描述', '')}
- 已有边界:{gap.get('已有边界', '')}
- 缺失边界:{gap.get('缺失边界', '')}
【要求】
1. 专门针对该边界条件设计测试步骤
2. 预期结果要明确描述边界值的行为
3. 考虑边界值附近的行为
请输出JSON格式的测试用例:
"""
prompt
+=
json
.
dumps
({
col
:
""
for
col
in
TEST_CASE_COLUMNS
},
ensure_ascii
=
False
)
prompt
+=
f
"""
注意:
1. 必须输出合法的JSON格式
2. 用例名称:{gap.get('建议用例名称', f'测试{gap.get("功能描述", "")}{gap.get("缺失边界", "")}')}
3. 用例等级:{gap.get('优先级', DEFAULT_CASE_LEVEL)}
4. STEP要详细描述如何测试该边界条件
5. 预期结果要描述边界值和附近值的预期行为
"""
return
self
.
_call_ai_for_case
(
prompt
,
gap
.
get
(
"缺失边界"
,
""
))
def
_call_ai_for_case
(
self
,
prompt
:
str
,
description
:
str
)
->
Optional
[
Dict
]:
"""
调用AI生成单个测试用例
Args:
prompt: 提示词
description: 用例描述(用于日志)
Returns:
测试用例字典,失败返回None
"""
try
:
# 使用AI服务管理器调用
case
=
self
.
ai_service
.
ask_json
(
prompt
,
AI_MODEL
)
# 类型检查:确保返回的是字典(兼容智谱AI等返回数组的情况)
if
isinstance
(
case
,
list
):
logger
.
debug
(
"AI返回了数组,长度:
%
d"
,
len
(
case
))
if
len
(
case
)
>
0
and
isinstance
(
case
[
0
],
dict
):
logger
.
info
(
"AI返回了数组,取第一个元素作为测试用例"
)
case
=
case
[
0
]
else
:
logger
.
error
(
"AI返回的数组格式不符合预期,第一个元素类型:
%
s"
,
type
(
case
[
0
])
if
len
(
case
)
>
0
else
"空数组"
)
return
None
if
not
isinstance
(
case
,
dict
):
logger
.
error
(
"AI返回的不是字典对象:
%
s"
,
type
(
case
))
logger
.
debug
(
"返回内容:
%
s"
,
str
(
case
)[:
200
])
return
None
# 设置默认值
if
not
case
.
get
(
"测试频次"
):
case
[
"测试频次"
]
=
DEFAULT_TEST_FREQUENCY
if
not
case
.
get
(
"JSON"
):
case
[
"JSON"
]
=
"暂不涉及"
return
case
except
Exception
as
e
:
logger
.
error
(
"生成测试用例失败(
%
s):
%
s"
,
description
,
e
)
logger
.
debug
(
"错误详情"
,
exc_info
=
True
)
return
None
def
_parse_case_result
(
self
,
result
:
str
)
->
Optional
[
Dict
]:
"""
解析AI返回的测试用例
Args:
result: AI返回的文本
Returns:
测试用例字典,失败返回None
"""
import
re
logger
.
debug
(
"尝试解析测试用例JSON,内容长度:
%
d"
,
len
(
result
))
# 尝试直接解析
try
:
case
=
json
.
loads
(
result
)
return
self
.
_validate_case
(
case
)
except
json
.
JSONDecodeError
:
pass
# 尝试从代码块中提取
code_block_pattern
=
r'```(?:json)?\s*\n?([\s\S]*?)\n?```'
matches
=
re
.
findall
(
code_block_pattern
,
result
)
for
match
in
matches
:
try
:
case
=
json
.
loads
(
match
.
strip
())
return
self
.
_validate_case
(
case
)
except
:
continue
# 尝试提取花括号内容(对象)- 使用括号匹配处理嵌套
brace_count
=
0
start_idx
=
-
1
for
i
,
char
in
enumerate
(
result
):
if
char
==
'{'
:
if
brace_count
==
0
:
start_idx
=
i
brace_count
+=
1
elif
char
==
'}'
:
brace_count
-=
1
if
brace_count
==
0
and
start_idx
>=
0
:
try
:
json_str
=
result
[
start_idx
:
i
+
1
]
case
=
json
.
loads
(
json_str
)
return
self
.
_validate_case
(
case
)
except
:
break
logger
.
warning
(
"未找到有效的JSON对象格式"
)
logger
.
debug
(
"AI返回内容:
%
s"
,
result
[:
500
])
return
None
def
_validate_case
(
self
,
case
:
Dict
)
->
Optional
[
Dict
]:
"""
验证测试用例格式
Args:
case: 测试用例字典
Returns:
验证通过的用例,失败返回None
"""
# 检查必填字段
required_fields
=
[
"用例名称"
,
"功能描述"
,
"STEP"
,
"预期结果"
]
for
field
in
required_fields
:
if
not
case
.
get
(
field
):
logger
.
warning
(
"测试用例缺少必填字段:
%
s"
,
field
)
# 设置默认值
if
field
==
"用例名称"
:
case
[
field
]
=
"自动生成的测试用例"
elif
field
==
"STEP"
:
case
[
field
]
=
"1. 执行操作
\n
2. 验证结果"
elif
field
==
"预期结果"
:
case
[
field
]
=
"操作成功,结果符合预期"
# 确保所有列都存在
for
col
in
TEST_CASE_COLUMNS
:
if
col
not
in
case
:
case
[
col
]
=
""
return
case
def
format_case_number
(
module
:
str
,
seq
:
int
)
->
str
:
"""
格式化用例编号
Args:
module: 功能模块
seq: 序号
Returns:
用例编号
"""
# 提取模块简称(前4个字符,大写)
module_short
=
module
[:
4
]
.
upper
()
if
module
else
"TEST"
# 去除中文字符
module_short
=
re
.
sub
(
r'[^\w]'
,
''
,
module_short
)
if
not
module_short
:
module_short
=
"TEST"
return
f
"TC-{module_short}-{seq:03d}"
AuxiliaryTool/AIPerfectedTestCases/src/claude_cli_adapter.py
0 → 100644
浏览文件 @
ed7ea006
# -*- coding: utf-8 -*-
"""
Claude CLI适配器模块
通过subprocess调用claude命令行工具
"""
import
subprocess
import
json
from
typing
import
Optional
,
Dict
,
Any
,
List
from
logging
import
getLogger
logger
=
getLogger
(
__name__
)
class
ClaudeCLIAdapter
:
"""
Claude CLI适配器
通过调用本地的claude命令行工具来使用AI能力
"""
def
__init__
(
self
,
timeout
:
int
=
300
):
"""
初始化适配器
Args:
timeout: 超时时间(秒),默认5分钟
"""
self
.
timeout
=
timeout
self
.
available
=
None
self
.
version
=
None
logger
.
info
(
"Claude CLI适配器初始化完成"
)
def
check_available
(
self
)
->
bool
:
"""
检查claude命令是否可用
Returns:
True表示可用,False表示不可用
"""
if
self
.
available
is
not
None
:
return
self
.
available
try
:
result
=
subprocess
.
run
(
[
"claude"
,
"--version"
],
capture_output
=
True
,
text
=
True
,
timeout
=
10
,
encoding
=
"utf-8"
)
if
result
.
returncode
==
0
:
self
.
version
=
result
.
stdout
.
strip
()
self
.
available
=
True
logger
.
info
(
"Claude CLI可用,版本:
%
s"
,
self
.
version
)
return
True
else
:
self
.
available
=
False
logger
.
warning
(
"Claude CLI命令返回错误:
%
s"
,
result
.
stderr
)
return
False
except
FileNotFoundError
:
self
.
available
=
False
logger
.
error
(
"未找到claude命令,请确认已安装Claude Code CLI"
)
logger
.
error
(
"安装方式: npm install -g @anthropic-ai/claude-code"
)
return
False
except
Exception
as
e
:
self
.
available
=
False
logger
.
error
(
"检查claude命令失败:
%
s"
,
e
)
return
False
def
ask
(
self
,
prompt
:
str
,
model
:
Optional
[
str
]
=
None
)
->
str
:
"""
调用claude ask命令
Args:
prompt: 提示词
model: 模型名称(可选,默认使用claude配置的默认模型)
Returns:
AI回复文本
"""
if
not
self
.
check_available
():
raise
RuntimeError
(
"Claude CLI不可用"
)
logger
.
info
(
"调用Claude CLI..."
)
logger
.
debug
(
"提示词长度:
%
d 字符"
,
len
(
prompt
))
# 构建命令
cmd
=
[
"claude"
,
"ask"
]
if
model
:
cmd
.
extend
([
"--model"
,
model
])
cmd
.
append
(
prompt
)
try
:
result
=
subprocess
.
run
(
cmd
,
capture_output
=
True
,
text
=
True
,
timeout
=
self
.
timeout
,
encoding
=
"utf-8"
)
if
result
.
returncode
==
0
:
response
=
result
.
stdout
.
strip
()
logger
.
info
(
"Claude CLI调用成功,返回
%
d 字符"
,
len
(
response
))
return
response
else
:
error_msg
=
result
.
stderr
.
strip
()
logger
.
error
(
"Claude CLI调用失败:
%
s"
,
error_msg
)
raise
RuntimeError
(
f
"Claude CLI error: {error_msg}"
)
except
subprocess
.
TimeoutExpired
:
logger
.
error
(
"Claude CLI调用超时(
%
d秒)"
,
self
.
timeout
)
raise
TimeoutError
(
f
"Claude CLI调用超时({self.timeout}秒)"
)
except
Exception
as
e
:
logger
.
error
(
"Claude CLI调用异常:
%
s"
,
e
)
raise
def
ask_json
(
self
,
prompt
:
str
,
model
:
Optional
[
str
]
=
None
)
->
Dict
:
"""
调用claude ask命令,并解析JSON返回
Args:
prompt: 提示词
model: 模型名称(可选)
Returns:
解析后的JSON对象
"""
# 确保提示词要求JSON格式
if
"JSON"
not
in
prompt
and
"json"
not
in
prompt
.
lower
():
if
prompt
.
endswith
(
"。"
):
prompt
=
prompt
[:
-
1
]
+
",请输出JSON格式的结果。"
else
:
prompt
=
prompt
+
"
\n\n
请输出JSON格式的结果。"
response
=
self
.
ask
(
prompt
,
model
)
return
self
.
_parse_json_response
(
response
)
def
ask_json_array
(
self
,
prompt
:
str
,
model
:
Optional
[
str
]
=
None
)
->
List
[
Dict
]:
"""
调用claude ask命令,并解析JSON数组返回
Args:
prompt: 提示词
model: 模型名称(可选)
Returns:
解析后的JSON数组
"""
# 确保提示词要求JSON数组格式
if
"JSON"
not
in
prompt
and
"json"
not
in
prompt
.
lower
():
if
prompt
.
endswith
(
"。"
):
prompt
=
prompt
[:
-
1
]
+
",请输出JSON数组格式的结果。"
else
:
prompt
=
prompt
+
"
\n\n
请输出JSON数组格式的结果。"
response
=
self
.
ask
(
prompt
,
model
)
return
self
.
_parse_json_array_response
(
response
)
def
_parse_json_response
(
self
,
response
:
str
)
->
Dict
:
"""
解析JSON响应
Args:
response: AI返回的文本
Returns:
解析后的JSON对象
"""
try
:
return
json
.
loads
(
response
)
except
json
.
JSONDecodeError
:
# 尝试提取JSON部分
try
:
start
=
response
.
find
(
"{"
)
end
=
response
.
rfind
(
"}"
)
+
1
if
start
>=
0
and
end
>
start
:
json_str
=
response
[
start
:
end
]
return
json
.
loads
(
json_str
)
except
:
pass
logger
.
error
(
"无法解析JSON响应"
)
logger
.
debug
(
"响应内容:
%
s"
,
response
[:
500
])
raise
ValueError
(
"响应不是有效的JSON格式"
)
def
_parse_json_array_response
(
self
,
response
:
str
)
->
List
:
"""
解析JSON数组响应
Args:
response: AI返回的文本
Returns:
解析后的JSON数组
"""
try
:
result
=
json
.
loads
(
response
)
if
isinstance
(
result
,
list
):
return
result
else
:
# 如果返回的是对象,尝试提取数组字段
if
isinstance
(
result
,
dict
):
for
key
,
value
in
result
.
items
():
if
isinstance
(
value
,
list
):
logger
.
info
(
"从JSON对象中提取数组:
%
s"
,
key
)
return
value
return
[
result
]
except
json
.
JSONDecodeError
:
# 尝试提取JSON部分
try
:
start
=
response
.
find
(
"["
)
end
=
response
.
rfind
(
"]"
)
+
1
if
start
>=
0
and
end
>
start
:
json_str
=
response
[
start
:
end
]
return
json
.
loads
(
json_str
)
except
:
pass
logger
.
error
(
"无法解析JSON数组响应"
)
logger
.
debug
(
"响应内容:
%
s"
,
response
[:
500
])
raise
ValueError
(
"响应不是有效的JSON数组格式"
)
def
test_claude_cli
()
->
Dict
:
"""
测试Claude CLI连接
Returns:
测试结果字典
"""
adapter
=
ClaudeCLIAdapter
()
result
=
{
"available"
:
False
,
"version"
:
None
,
"error"
:
None
}
try
:
if
adapter
.
check_available
():
result
[
"available"
]
=
True
result
[
"version"
]
=
adapter
.
version
# 测试调用
logger
.
info
(
"发送测试请求..."
)
response
=
adapter
.
ask
(
"请回复:测试成功"
,
model
=
"claude-sonnet-4-6"
)
if
"测试成功"
in
response
or
"test"
in
response
.
lower
():
result
[
"test_passed"
]
=
True
else
:
result
[
"test_passed"
]
=
False
result
[
"response"
]
=
response
else
:
result
[
"error"
]
=
"Claude CLI不可用"
except
Exception
as
e
:
result
[
"error"
]
=
str
(
e
)
return
result
# 便捷函数
def
call_claude
(
prompt
:
str
,
model
:
Optional
[
str
]
=
None
,
timeout
:
int
=
300
)
->
str
:
"""
便捷函数:调用Claude CLI
Args:
prompt: 提示词
model: 模型名称(可选)
timeout: 超时时间(秒)
Returns:
AI回复文本
"""
adapter
=
ClaudeCLIAdapter
(
timeout
=
timeout
)
return
adapter
.
ask
(
prompt
,
model
)
def
call_claude_json
(
prompt
:
str
,
model
:
Optional
[
str
]
=
None
,
timeout
:
int
=
300
)
->
Dict
:
"""
便捷函数:调用Claude CLI并返回JSON
Args:
prompt: 提示词
model: 模型名称(可选)
timeout: 超时时间(秒)
Returns:
解析后的JSON对象
"""
adapter
=
ClaudeCLIAdapter
(
timeout
=
timeout
)
return
adapter
.
ask_json
(
prompt
,
model
)
AuxiliaryTool/AIPerfectedTestCases/src/config.py
0 → 100644
浏览文件 @
ed7ea006
# -*- coding: utf-8 -*-
"""
AI完善测试用例工具 - 配置模块
本模块定义项目所需的配置常量和路径
"""
from
pathlib
import
Path
# ==================== 项目路径 ====================
# 项目根目录
PROJECT_ROOT
=
Path
(
__file__
)
.
parent
.
parent
# 配置目录
CONFIG_DIR
=
PROJECT_ROOT
/
"config"
# 输出目录
PERFECTED_DIR
=
PROJECT_ROOT
/
"Perfected"
REPORTS_DIR
=
PROJECT_ROOT
/
"reports"
LOGS_DIR
=
PROJECT_ROOT
/
"logs"
# ==================== 输入路径 ====================
# 需求文档目录
REQUIREMENTS_DIR
=
CONFIG_DIR
/
"需求文档"
# 开发PRD文档目录
PRD_DIR
=
CONFIG_DIR
/
"开发PRD"
# 测试用例文件
TEST_CASE_FILE
=
CONFIG_DIR
/
"测试用例"
/
"新统一平台权限管理测试用例.xlsx"
# ==================== 输出路径 ====================
# 输出测试用例文件
OUTPUT_TEST_CASE
=
PERFECTED_DIR
/
"新统一平台权限管理测试用例_完善版本.xlsx"
# 输出差异性报告文件
OUTPUT_REPORT
=
REPORTS_DIR
/
"新统一平台权限管理测试用例_差异性报告.docx"
# ==================== 测试用例列定义 ====================
# 测试用例Excel表格的列名
TEST_CASE_COLUMNS
=
[
"序列号"
,
# 序号,用于排序
"功能模块"
,
# 功能模块名称,用于分类
"用例编号"
,
# 用例编号,用于标识用例
"功能描述"
,
# 功能描述,用于描述功能
"用例等级"
,
# 用例等级,用于描述用例的优先级
"功能编号"
,
# 功能编号,用于标识功能
"用例名称"
,
# 用例名称,用于标识用例
"预置条件"
,
# 测试用例的预置条件,用于准备测试环境
"STEP"
,
# 步骤描述
"JSON"
,
# 自动化测试用例,暂不涉及
"预期结果"
,
# 测试用例的预期结果,用于描述测试结果
"测试结果"
,
# 测试用例的测试结果,用于描述测试结果
"测试频次"
,
# 测试用例的测试频次,用于描述测试用例的测试频次
"日志/截图/照片"
,
# 用于记录测试过程中产生的日志、截图、照片
"备注"
# 用于记录测试过程中产生的问题,用于问题排查
]
# ==================== AI配置 ====================
# AI模型选择
# 可选值: "claude-sonnet-4-6", "claude-opus-4-6"
AI_MODEL
=
"claude-sonnet-4-6"
# AI最大token数
AI_MAX_TOKENS
=
8192
# AI温度参数(较低温度确保稳定性)
AI_TEMPERATURE
=
0.3
# API密钥环境变量名
API_KEY_ENV
=
"ANTHROPIC_API_KEY"
# API基础URL(可选,用于代理服务)
# 官方API: https://api.anthropic.com
# CCSwitch等代理: 根据服务商提供的地址配置
API_BASE_URL
=
"https://api.anthropic.com"
# ==================== AI服务模式 ====================
# AI服务模式:auto(自动检测)、api(Anthropic API)、cli(Claude CLI)
AI_SERVICE_MODE
=
"auto"
# Claude CLI超时时间(秒)
CLI_TIMEOUT
=
300
# API密钥格式前缀(用于验证)
API_KEY_PREFIX
=
"sk-ant-api03-"
# ==================== 覆盖率目标 ====================
# 场景覆盖率目标(90%)
COVERAGE_TARGET
=
0.9
# ==================== 日志配置 ====================
# 日志格式
LOG_FORMAT
=
"
%(asctime)
s -
%(name)
s -
%(levelname)
s -
%(message)
s"
# 日志日期格式
LOG_DATE_FORMAT
=
"
%
Y-
%
m-
%
d
%
H:
%
M:
%
S"
# 日志文件名
LOG_FILE
=
LOGS_DIR
/
"ai_test_case_enhancer.log"
# ==================== 用例编号格式 ====================
# 用例编号前缀
CASE_NUMBER_PREFIX
=
"TC"
# ==================== 用例等级定义 ====================
# 用例等级选项
CASE_LEVELS
=
[
"P0"
,
"P1"
,
"P2"
,
"P3"
]
# 默认用例等级
DEFAULT_CASE_LEVEL
=
"P1"
# ==================== 测试频次定义 ====================
# 测试频次选项
TEST_FREQUENCIES
=
[
"每次"
,
"每日"
,
"每周"
,
"每月"
,
"每版本"
,
"一次性"
]
# 默认测试频次
DEFAULT_TEST_FREQUENCY
=
"每版本"
def
ensure_directories
()
->
None
:
"""
确保所有必要的目录存在
"""
PERFECTED_DIR
.
mkdir
(
parents
=
True
,
exist_ok
=
True
)
REPORTS_DIR
.
mkdir
(
parents
=
True
,
exist_ok
=
True
)
LOGS_DIR
.
mkdir
(
parents
=
True
,
exist_ok
=
True
)
def
get_project_info
()
->
dict
:
"""
获取项目信息
Returns:
项目信息字典
"""
return
{
"project_root"
:
str
(
PROJECT_ROOT
),
"config_dir"
:
str
(
CONFIG_DIR
),
"requirements_dir"
:
str
(
REQUIREMENTS_DIR
),
"prd_dir"
:
str
(
PRD_DIR
),
"test_case_file"
:
str
(
TEST_CASE_FILE
),
"output_test_case"
:
str
(
OUTPUT_TEST_CASE
),
"output_report"
:
str
(
OUTPUT_REPORT
),
"ai_model"
:
AI_MODEL
,
"coverage_target"
:
COVERAGE_TARGET
}
def
get_ai_service_mode
()
->
str
:
"""
获取AI服务模式
优先级:环境变量 > 配置文件常量
Returns:
服务模式:'auto', 'api', 'cli'
"""
import
os
mode
=
os
.
environ
.
get
(
"AI_SERVICE_MODE"
,
AI_SERVICE_MODE
)
.
lower
()
# 验证模式值
if
mode
not
in
(
"auto"
,
"api"
,
"cli"
):
return
AI_SERVICE_MODE
return
mode
def
get_api_base_url
()
->
str
:
"""
获取API基础URL
优先级:环境变量 > 配置文件常量
Returns:
API基础URL
"""
import
os
return
os
.
environ
.
get
(
"API_BASE_URL"
,
API_BASE_URL
)
AuxiliaryTool/AIPerfectedTestCases/src/deduplicator.py
0 → 100644
浏览文件 @
ed7ea006
# -*- coding: utf-8 -*-
"""
去重模块
本模块负责对生成的测试用例进行去重处理
"""
from
typing
import
List
,
Dict
,
Set
from
logging
import
getLogger
import
hashlib
logger
=
getLogger
(
__name__
)
class
Deduplicator
:
"""
测试用例去重器
主要功能:
1. 检测重复的测试用例
2. 保留唯一用例
3. 生成去重报告
"""
def
__init__
(
self
):
"""初始化去重器"""
logger
.
info
(
"测试用例去重器初始化完成"
)
def
deduplicate
(
self
,
existing_cases
:
List
[
Dict
],
new_cases
:
List
[
Dict
])
->
Dict
:
"""
去重处理
Args:
existing_cases: 现有测试用例列表
new_cases: 新生成的测试用例列表
Returns:
去重结果,包含:
- unique_cases: 唯一用例列表
- duplicate_cases: 重复用例列表
- duplicate_count: 重复用例数量
- summary: 去重统计
"""
logger
.
info
(
"开始去重处理..."
)
logger
.
info
(
"现有用例:
%
d 条,新生成用例:
%
d 条"
,
len
(
existing_cases
),
len
(
new_cases
))
# 构建现有用例的指纹集合
existing_fingerprints
=
set
()
for
case
in
existing_cases
:
fp
=
self
.
_generate_fingerprint
(
case
)
existing_fingerprints
.
add
(
fp
)
logger
.
debug
(
"现有用例指纹数:
%
d"
,
len
(
existing_fingerprints
))
# 筛选新用例
unique_cases
=
[]
duplicate_cases
=
[]
new_fingerprints
=
set
()
for
i
,
case
in
enumerate
(
new_cases
):
fp
=
self
.
_generate_fingerprint
(
case
)
# 检查是否与现有用例重复
if
fp
in
existing_fingerprints
:
duplicate_cases
.
append
({
**
case
,
"_duplicate_type"
:
"existing"
,
"_fingerprint"
:
fp
})
logger
.
debug
(
"新用例
%
d 与现有用例重复"
,
i
+
1
)
continue
# 检查是否与新用例列表中的其他用例重复
if
fp
in
new_fingerprints
:
duplicate_cases
.
append
({
**
case
,
"_duplicate_type"
:
"new"
,
"_fingerprint"
:
fp
})
logger
.
debug
(
"新用例
%
d 在新用例列表中重复"
,
i
+
1
)
continue
# 唯一用例
unique_cases
.
append
(
case
)
new_fingerprints
.
add
(
fp
)
# 生成统计报告
summary
=
{
"existing_cases"
:
len
(
existing_cases
),
"new_cases"
:
len
(
new_cases
),
"unique_cases"
:
len
(
unique_cases
),
"duplicate_with_existing"
:
len
([
c
for
c
in
duplicate_cases
if
c
.
get
(
"_duplicate_type"
)
==
"existing"
]),
"duplicate_within_new"
:
len
([
c
for
c
in
duplicate_cases
if
c
.
get
(
"_duplicate_type"
)
==
"new"
]),
"total_duplicates"
:
len
(
duplicate_cases
),
"deduplication_rate"
:
len
(
unique_cases
)
/
len
(
new_cases
)
if
new_cases
else
0
}
logger
.
info
(
"去重完成:"
)
logger
.
info
(
" 唯一新用例:
%
d 条"
,
summary
[
"unique_cases"
])
logger
.
info
(
" 与现有用例重复:
%
d 条"
,
summary
[
"duplicate_with_existing"
])
logger
.
info
(
" 新用例内部重复:
%
d 条"
,
summary
[
"duplicate_within_new"
])
logger
.
info
(
" 总计重复:
%
d 条"
,
summary
[
"total_duplicates"
])
logger
.
info
(
" 去重率:
%.1
f
%%
"
,
summary
[
"deduplication_rate"
]
*
100
)
return
{
"unique_cases"
:
unique_cases
,
"duplicate_cases"
:
duplicate_cases
,
"summary"
:
summary
}
def
_generate_fingerprint
(
self
,
case
:
Dict
)
->
str
:
"""
生成用例指纹(用于去重)
Args:
case: 测试用例字典
Returns:
用例指纹字符串
"""
# 使用关键字段生成唯一标识
key_parts
=
[
str
(
case
.
get
(
"功能描述"
,
""
))
.
strip
(),
str
(
case
.
get
(
"用例名称"
,
""
))
.
strip
(),
str
(
case
.
get
(
"功能编号"
,
""
))
.
strip
()
]
# 将关键字段拼接后生成哈希
content
=
"|"
.
join
(
key_parts
)
.
strip
()
.
lower
()
# 如果内容为空,返回空字符串
if
not
content
:
return
""
# 生成MD5哈希
return
hashlib
.
md5
(
content
.
encode
(
'utf-8'
))
.
hexdigest
()
def
fuzzy_deduplicate
(
self
,
cases
:
List
[
Dict
],
similarity_threshold
:
float
=
0.85
)
->
Dict
:
"""
模糊去重(基于相似度)
Args:
cases: 测试用例列表
similarity_threshold: 相似度阈值
Returns:
模糊去重结果
"""
logger
.
info
(
"开始模糊去重(相似度阈值:
%.2
f)..."
,
similarity_threshold
)
unique_cases
=
[]
duplicate_cases
=
[]
checked_indices
=
set
()
for
i
,
case1
in
enumerate
(
cases
):
if
i
in
checked_indices
:
continue
is_duplicate
=
False
for
j
,
case2
in
enumerate
(
cases
):
if
i
>=
j
or
j
in
checked_indices
:
continue
similarity
=
self
.
_calculate_similarity
(
case1
,
case2
)
if
similarity
>=
similarity_threshold
:
# case2 与 case1 相似,标记为重复
duplicate_cases
.
append
({
**
case2
,
"_duplicate_type"
:
"fuzzy"
,
"_similar_to"
:
i
,
"_similarity"
:
similarity
})
checked_indices
.
add
(
j
)
is_duplicate
=
True
logger
.
debug
(
"用例
%
d 与用例
%
d 相似(
%.2
f)"
,
i
,
j
,
similarity
)
if
not
is_duplicate
:
unique_cases
.
append
(
case1
)
checked_indices
.
add
(
i
)
logger
.
info
(
"模糊去重完成:"
)
logger
.
info
(
" 唯一用例:
%
d 条"
,
len
(
unique_cases
))
logger
.
info
(
" 相似重复:
%
d 条"
,
len
(
duplicate_cases
))
return
{
"unique_cases"
:
unique_cases
,
"duplicate_cases"
:
duplicate_cases
}
def
_calculate_similarity
(
self
,
case1
:
Dict
,
case2
:
Dict
)
->
float
:
"""
计算两个测试用例的相似度
Args:
case1: 测试用例1
case2: 测试用例2
Returns:
相似度(0-1之间)
"""
# 简单的文本相似度计算
def
text_similarity
(
text1
:
str
,
text2
:
str
)
->
float
:
"""计算两个文本的相似度"""
if
not
text1
or
not
text2
:
return
0.0
words1
=
set
(
text1
.
lower
()
.
split
())
words2
=
set
(
text2
.
lower
()
.
split
())
if
not
words1
or
not
words2
:
return
0.0
intersection
=
words1
&
words2
union
=
words1
|
words2
return
len
(
intersection
)
/
len
(
union
)
if
union
else
0.0
# 比较多个字段
fields
=
[
"用例名称"
,
"功能描述"
,
"预期结果"
]
similarities
=
[]
for
field
in
fields
:
text1
=
str
(
case1
.
get
(
field
,
""
))
text2
=
str
(
case2
.
get
(
field
,
""
))
sim
=
text_similarity
(
text1
,
text2
)
similarities
.
append
(
sim
)
# 返回平均相似度
return
sum
(
similarities
)
/
len
(
similarities
)
if
similarities
else
0.0
def
find_similar_cases
(
self
,
target_case
:
Dict
,
case_list
:
List
[
Dict
],
top_n
:
int
=
5
)
->
List
[
Dict
]:
"""
查找相似的测试用例
Args:
target_case: 目标测试用例
case_list: 测试用例列表
top_n: 返回前N个最相似的用例
Returns:
相似用例列表,每项包含用例和相似度
"""
similarities
=
[]
for
i
,
case
in
enumerate
(
case_list
):
sim
=
self
.
_calculate_similarity
(
target_case
,
case
)
similarities
.
append
({
"index"
:
i
,
"case"
:
case
,
"similarity"
:
sim
})
# 按相似度降序排序
similarities
.
sort
(
key
=
lambda
x
:
x
[
"similarity"
],
reverse
=
True
)
return
similarities
[:
top_n
]
def
merge_test_cases
(
existing_cases
:
List
[
Dict
],
new_cases
:
List
[
Dict
])
->
List
[
Dict
]:
"""
合并测试用例(不进行去重)
Args:
existing_cases: 现有测试用例列表
new_cases: 新测试用例列表
Returns:
合并后的测试用例列表
"""
# 更新序列号
max_seq
=
0
for
case
in
existing_cases
:
seq
=
case
.
get
(
"序列号"
)
if
isinstance
(
seq
,
int
):
max_seq
=
max
(
max_seq
,
seq
)
# 为新用例分配序列号
for
i
,
case
in
enumerate
(
new_cases
):
max_seq
+=
1
case
[
"序列号"
]
=
max_seq
return
existing_cases
+
new_cases
AuxiliaryTool/AIPerfectedTestCases/src/document_reader.py
0 → 100644
浏览文件 @
ed7ea006
# -*- coding: utf-8 -*-
"""
文档读取模块
本模块负责读取需求文档(.docx)、开发PRD文档(.md)和测试用例(.xlsx)
"""
from
pathlib
import
Path
from
typing
import
List
,
Dict
,
Optional
from
logging
import
getLogger
try
:
from
docx
import
Document
except
ImportError
:
print
(
"警告: 未安装python-docx库,请运行: pip install python-docx"
)
raise
try
:
import
openpyxl
except
ImportError
:
print
(
"警告: 未安装openpyxl库,请运行: pip install openpyxl"
)
raise
logger
=
getLogger
(
__name__
)
class
DocumentReader
:
"""
文档读取器
负责读取三种类型的文档:
1. 需求文档(.docx格式)
2. 开发PRD文档(.md格式)
3. 测试用例(.xlsx格式)
"""
def
__init__
(
self
):
"""初始化文档读取器"""
logger
.
info
(
"文档读取器初始化完成"
)
def
read_requirement_docs
(
self
,
requirements_dir
:
Path
)
->
List
[
Dict
]:
"""
读取需求文档(.docx)
Args:
requirements_dir: 需求文档目录路径
Returns:
需求文档列表,每项包含:
- filename: 文件名
- path: 文件路径
- content: 文档内容
"""
if
not
requirements_dir
.
exists
():
logger
.
warning
(
"需求文档目录不存在:
%
s"
,
requirements_dir
)
return
[]
docs
=
[]
docx_files
=
list
(
requirements_dir
.
glob
(
"*.docx"
))
if
not
docx_files
:
logger
.
warning
(
"需求文档目录下没有找到.docx文件:
%
s"
,
requirements_dir
)
return
[]
logger
.
info
(
"找到
%
d 份需求文档"
,
len
(
docx_files
))
for
docx_file
in
docx_files
:
try
:
logger
.
info
(
"读取需求文档:
%
s"
,
docx_file
.
name
)
doc
=
Document
(
docx_file
)
# 提取所有段落文本
paragraphs
=
[]
for
para
in
doc
.
paragraphs
:
if
para
.
text
.
strip
():
paragraphs
.
append
(
para
.
text
.
strip
())
# 提取表格内容
tables_content
=
[]
for
table
in
doc
.
tables
:
for
row
in
table
.
rows
:
row_text
=
" | "
.
join
([
cell
.
text
.
strip
()
for
cell
in
row
.
cells
])
if
row_text
.
strip
():
tables_content
.
append
(
row_text
)
content
=
"
\n
"
.
join
(
paragraphs
)
if
tables_content
:
content
+=
"
\n\n
【表格内容】
\n
"
+
"
\n
"
.
join
(
tables_content
)
docs
.
append
({
"filename"
:
docx_file
.
name
,
"path"
:
str
(
docx_file
),
"content"
:
content
})
logger
.
debug
(
"文档
%
s 读取完成,内容长度:
%
d 字符"
,
docx_file
.
name
,
len
(
content
))
except
Exception
as
e
:
logger
.
error
(
"读取需求文档失败:
%
s, 错误:
%
s"
,
docx_file
.
name
,
e
)
logger
.
info
(
"需求文档读取完成,共
%
d 份"
,
len
(
docs
))
return
docs
def
read_prd_docs
(
self
,
prd_dir
:
Path
)
->
List
[
Dict
]:
"""
读取开发PRD文档(.md)
Args:
prd_dir: PRD文档目录路径
Returns:
PRD文档列表,每项包含:
- filename: 文件名
- path: 文件路径
- content: 文档内容
"""
if
not
prd_dir
.
exists
():
logger
.
warning
(
"PRD文档目录不存在:
%
s"
,
prd_dir
)
return
[]
docs
=
[]
md_files
=
list
(
prd_dir
.
glob
(
"*.md"
))
if
not
md_files
:
logger
.
warning
(
"PRD文档目录下没有找到.md文件:
%
s"
,
prd_dir
)
return
[]
logger
.
info
(
"找到
%
d 份PRD文档"
,
len
(
md_files
))
for
md_file
in
md_files
:
try
:
logger
.
info
(
"读取PRD文档:
%
s"
,
md_file
.
name
)
with
open
(
md_file
,
"r"
,
encoding
=
"utf-8"
)
as
f
:
content
=
f
.
read
()
docs
.
append
({
"filename"
:
md_file
.
name
,
"path"
:
str
(
md_file
),
"content"
:
content
})
logger
.
debug
(
"文档
%
s 读取完成,内容长度:
%
d 字符"
,
md_file
.
name
,
len
(
content
))
except
Exception
as
e
:
logger
.
error
(
"读取PRD文档失败:
%
s, 错误:
%
s"
,
md_file
.
name
,
e
)
logger
.
info
(
"PRD文档读取完成,共
%
d 份"
,
len
(
docs
))
return
docs
def
read_test_cases
(
self
,
test_case_file
:
Path
)
->
List
[
Dict
]:
"""
读取测试用例(.xlsx)
Args:
test_case_file: 测试用例文件路径
Returns:
测试用例列表,每项是一个字典,键为列名
"""
if
not
test_case_file
.
exists
():
logger
.
error
(
"测试用例文件不存在:
%
s"
,
test_case_file
)
raise
FileNotFoundError
(
f
"测试用例文件不存在: {test_case_file}"
)
logger
.
info
(
"读取测试用例:
%
s"
,
test_case_file
.
name
)
try
:
# 使用read_only和数据模式提高读取可靠性
wb
=
openpyxl
.
load_workbook
(
test_case_file
,
read_only
=
True
,
data_only
=
True
)
# 打印工作表信息
logger
.
info
(
"工作表列表:
%
s"
,
wb
.
sheetnames
)
# 使用活动工作表
ws
=
wb
.
active
logger
.
info
(
"使用工作表:
%
s (大小:
%
d行 x
%
d列)"
,
ws
.
title
,
ws
.
max_row
,
ws
.
max_column
)
# 查找表头行
headers
=
None
header_row
=
1
for
row_idx
in
range
(
1
,
min
(
20
,
ws
.
max_row
+
1
)):
row_values
=
[
cell
.
value
for
cell
in
ws
[
row_idx
]]
# 检查是否包含"用例名称"列
if
any
(
cell
.
value
and
"用例名称"
in
str
(
cell
.
value
)
for
cell
in
ws
[
row_idx
]):
headers
=
row_values
header_row
=
row_idx
logger
.
info
(
"在第
%
d行找到表头:
%
s"
,
row_idx
,
headers
)
break
# 如果没找到表头,使用第1行
if
not
headers
:
logger
.
warning
(
"未找到包含'用例名称'的表头行,使用第1行作为表头"
)
headers
=
[
cell
.
value
for
cell
in
ws
[
1
]]
header_row
=
1
# 读取数据
cases
=
[]
data_start_row
=
header_row
+
1
empty_rows
=
0
valid_rows
=
0
for
row_idx
in
range
(
data_start_row
,
ws
.
max_row
+
1
):
row_values
=
[
cell
.
value
for
cell
in
ws
[
row_idx
]]
# 跳过全空行
if
not
any
(
row_values
):
empty_rows
+=
1
continue
# 构建用例字典
case
=
dict
(
zip
(
headers
,
row_values
))
case_name
=
case
.
get
(
"用例名称"
)
# 检查是否有有效内容
if
case_name
and
str
(
case_name
)
.
strip
():
case
[
"_row_number"
]
=
row_idx
cases
.
append
(
case
)
valid_rows
+=
1
elif
row_idx
<=
data_start_row
+
10
:
# 只打印前10行的调试信息
logger
.
debug
(
"第
%
d行:用例名称为空,跳过。行数据:
%
s"
,
row_idx
,
{
k
:
v
for
k
,
v
in
case
.
items
()
if
v
})
wb
.
close
()
logger
.
info
(
"测试用例读取完成:"
)
logger
.
info
(
" 有效用例:
%
d 条"
,
len
(
cases
))
logger
.
info
(
" 空行跳过:
%
d 行"
,
empty_rows
)
# 统计信息
if
cases
:
modules
=
set
()
for
case
in
cases
:
module
=
case
.
get
(
"功能模块"
)
if
module
:
modules
.
add
(
module
)
logger
.
info
(
" 功能模块:
%
s"
,
sorted
(
modules
))
return
cases
except
Exception
as
e
:
logger
.
error
(
"读取测试用例失败:
%
s"
,
e
)
import
traceback
logger
.
debug
(
traceback
.
format_exc
())
raise
def
read_all_documents
(
self
,
requirements_dir
:
Path
,
prd_dir
:
Path
,
test_case_file
:
Path
)
->
Dict
:
"""
一次性读取所有文档
Args:
requirements_dir: 需求文档目录
prd_dir: PRD文档目录
test_case_file: 测试用例文件
Returns:
包含所有文档的字典
"""
logger
.
info
(
"="
*
60
)
logger
.
info
(
"开始读取所有文档..."
)
logger
.
info
(
"="
*
60
)
requirements
=
self
.
read_requirement_docs
(
requirements_dir
)
prds
=
self
.
read_prd_docs
(
prd_dir
)
test_cases
=
self
.
read_test_cases
(
test_case_file
)
result
=
{
"requirements"
:
requirements
,
"prds"
:
prds
,
"test_cases"
:
test_cases
,
"summary"
:
{
"requirements_count"
:
len
(
requirements
),
"prds_count"
:
len
(
prds
),
"test_cases_count"
:
len
(
test_cases
)
}
}
logger
.
info
(
"="
*
60
)
logger
.
info
(
"所有文档读取完成:"
)
logger
.
info
(
" 需求文档:
%
d 份"
,
result
[
"summary"
][
"requirements_count"
])
logger
.
info
(
" PRD文档:
%
d 份"
,
result
[
"summary"
][
"prds_count"
])
logger
.
info
(
" 测试用例:
%
d 条"
,
result
[
"summary"
][
"test_cases_count"
])
logger
.
info
(
"="
*
60
)
return
result
def
validate_documents
(
requirements_dir
:
Path
,
prd_dir
:
Path
,
test_case_file
:
Path
)
->
Dict
[
str
,
str
]:
"""
验证文档是否存在
Args:
requirements_dir: 需求文档目录
prd_dir: PRD文档目录
test_case_file: 测试用例文件
Returns:
验证结果字典
"""
result
=
{
"requirements_dir"
:
str
(
requirements_dir
),
"prd_dir"
:
str
(
prd_dir
),
"test_case_file"
:
str
(
test_case_file
),
"valid"
:
True
,
"errors"
:
[]
}
# 检查需求文档目录
if
not
requirements_dir
.
exists
():
result
[
"valid"
]
=
False
result
[
"errors"
]
.
append
(
f
"需求文档目录不存在: {requirements_dir}"
)
# 检查PRD文档目录
if
not
prd_dir
.
exists
():
result
[
"valid"
]
=
False
result
[
"errors"
]
.
append
(
f
"PRD文档目录不存在: {prd_dir}"
)
# 检查测试用例文件
if
not
test_case_file
.
exists
():
result
[
"valid"
]
=
False
result
[
"errors"
]
.
append
(
f
"测试用例文件不存在: {test_case_file}"
)
# 检查需求文档
if
requirements_dir
.
exists
():
req_files
=
list
(
requirements_dir
.
glob
(
"*.docx"
))
if
not
req_files
:
result
[
"errors"
]
.
append
(
f
"需求文档目录下没有.docx文件: {requirements_dir}"
)
# 检查PRD文档
if
prd_dir
.
exists
():
prd_files
=
list
(
prd_dir
.
glob
(
"*.md"
))
if
not
prd_files
:
result
[
"errors"
]
.
append
(
f
"PRD文档目录下没有.md文件: {prd_dir}"
)
return
result
AuxiliaryTool/AIPerfectedTestCases/src/gap_detector.py
0 → 100644
浏览文件 @
ed7ea006
# -*- coding: utf-8 -*-
"""
缺失检测模块
本模块负责使用AI检测测试用例的缺失情况
"""
import
json
from
typing
import
List
,
Dict
,
Optional
from
logging
import
getLogger
from
src.config
import
AI_MODEL
,
COVERAGE_TARGET
logger
=
getLogger
(
__name__
)
class
GapDetector
:
"""
缺失检测器
主要功能:
1. 检测功能点缺失
2. 检测场景覆盖不足
3. 检测边界条件不足
支持两种AI服务模式:
- Anthropic API:使用官方API密钥
- Claude CLI:使用本地CLI工具
"""
def
__init__
(
self
,
api_key
:
Optional
[
str
]
=
None
,
use_cli
:
bool
=
False
):
"""
初始化检测器
Args:
api_key: Anthropic API密钥(可选,兼容性保留)
use_cli: 是否使用CLI模式
"""
from
src.ai_service_manager
import
AIServiceManager
# 使用AI服务管理器
self
.
ai_service
=
AIServiceManager
(
api_key
=
api_key
,
use_cli
=
use_cli
)
logger
.
info
(
"缺失检测器初始化完成"
)
def
detect_gaps
(
self
,
function_points
:
List
[
Dict
],
test_cases
:
List
[
Dict
],
requirements
:
List
[
Dict
],
prds
:
List
[
Dict
])
->
Dict
:
"""
检测测试用例缺失情况
Args:
function_points: 功能点列表
test_cases: 测试用例列表
requirements: 需求文档列表
prds: PRD文档列表
Returns:
缺失检测结果,包含:
- 功能点缺失: 缺失的功能点列表
- 场景覆盖不足: 场景覆盖不足的列表
- 边界条件不足: 边界条件不足的列表
"""
logger
.
info
(
"开始检测测试用例缺失..."
)
logger
.
info
(
" 功能点数:
%
d"
,
len
(
function_points
))
logger
.
info
(
" 测试用例数:
%
d"
,
len
(
test_cases
))
# 使用AI分析缺失情况
gaps
=
self
.
_ai_detect_gaps
(
function_points
,
test_cases
,
requirements
,
prds
)
return
gaps
return
gaps
def
_ai_detect_gaps
(
self
,
function_points
:
List
[
Dict
],
test_cases
:
List
[
Dict
],
requirements
:
List
[
Dict
],
prds
:
List
[
Dict
])
->
Dict
:
"""
使用AI检测缺失
Args:
function_points: 功能点列表
test_cases: 测试用例列表
requirements: 需求文档列表
prds: PRD文档列表
Returns:
缺失检测结果
"""
prompt
=
self
.
_build_gap_prompt
(
function_points
,
test_cases
,
requirements
,
prds
)
try
:
# 使用AI服务管理器调用
result
=
self
.
ai_service
.
ask_json_array
(
prompt
,
AI_MODEL
)
logger
.
debug
(
"AI返回结果类型:
%
s"
,
type
(
result
))
if
isinstance
(
result
,
dict
):
logger
.
debug
(
"AI返回的键:
%
s"
,
list
(
result
.
keys
()))
for
key
,
value
in
result
.
items
():
if
isinstance
(
value
,
list
):
logger
.
debug
(
"
%
s:
%
d 项"
,
key
,
len
(
value
))
# 处理返回结果
gaps
=
{
"功能点缺失"
:
[],
"场景覆盖不足"
:
[],
"边界条件不足"
:
[]}
if
isinstance
(
result
,
dict
):
# 直接是字典格式,合并结果
for
key
in
gaps
:
if
key
in
result
and
isinstance
(
result
[
key
],
list
):
gaps
[
key
]
=
result
[
key
]
elif
isinstance
(
result
,
list
):
if
len
(
result
)
>
0
:
if
isinstance
(
result
[
0
],
dict
):
# 列表中包含字典,可能是单个gap对象
first_item
=
result
[
0
]
for
key
in
gaps
:
if
key
in
first_item
and
isinstance
(
first_item
[
key
],
list
):
gaps
[
key
]
=
first_item
[
key
]
else
:
# 列表直接是功能点缺失
gaps
[
"功能点缺失"
]
=
result
logger
.
info
(
"AI检测完成:"
)
logger
.
info
(
" 功能点缺失:
%
d 项"
,
len
(
gaps
[
"功能点缺失"
]))
logger
.
info
(
" 场景覆盖不足:
%
d 项"
,
len
(
gaps
[
"场景覆盖不足"
]))
logger
.
info
(
" 边界条件不足:
%
d 项"
,
len
(
gaps
[
"边界条件不足"
]))
return
gaps
except
Exception
as
e
:
logger
.
error
(
"AI调用失败:
%
s"
,
e
)
logger
.
debug
(
"错误详情"
,
exc_info
=
True
)
return
{
"功能点缺失"
:
[],
"场景覆盖不足"
:
[],
"边界条件不足"
:
[]}
def
_build_gap_prompt
(
self
,
function_points
:
List
[
Dict
],
test_cases
:
List
[
Dict
],
requirements
:
List
[
Dict
],
prds
:
List
[
Dict
])
->
str
:
"""
构建缺失检测提示词
Args:
function_points: 功能点列表
test_cases: 测试用例列表
requirements: 需求文档列表
prds: PRD文档列表
Returns:
提示词字符串
"""
prompt
=
"""你是一个专业的测试用例分析专家。请分析以下功能点、需求文档、PRD文档和现有测试用例,识别测试用例的缺失情况。
【功能点列表】
"""
# 添加功能点
prompt
+=
json
.
dumps
(
function_points
,
ensure_ascii
=
False
,
indent
=
2
)
# 添加现有测试用例(限制数量)
prompt
+=
f
"
\n\n
【现有测试用例】(共{len(test_cases)}条,显示前30条)
\n
"
for
i
,
case
in
enumerate
(
test_cases
[:
30
],
1
):
prompt
+=
f
"{i}. 用例名称: {case.get('用例名称', '')}
\n
"
prompt
+=
f
" 功能描述: {case.get('功能描述', '')}
\n
"
prompt
+=
f
" 用例等级: {case.get('用例等级', '')}
\n\n
"
# 添加需求文档摘要
prompt
+=
"
\n
【需求文档摘要】
\n
"
for
req
in
requirements
:
prompt
+=
f
"- {req['filename']}: {req['content'][:500]}...
\n
"
# 添加PRD文档摘要
prompt
+=
"
\n
【PRD文档摘要】
\n
"
for
prd
in
prds
:
prompt
+=
f
"- {prd['filename']}: {prd['content'][:500]}...
\n
"
prompt
+=
"""
\n
请识别以下三类缺失,输出JSON格式:
{
"功能点缺失": [
{
"功能模块": "模块名",
"功能编号": "F-001",
"功能描述": "缺失的功能描述",
"建议用例名称": "测试XXX功能",
"优先级": "P1"
}
],
"场景覆盖不足": [
{
"功能描述": "XXX功能",
"已有场景": "正常场景",
"缺失场景": "异常场景:XXX",
"建议用例名称": "测试XXX异常场景",
"优先级": "P2"
}
],
"边界条件不足": [
{
"功能描述": "XXX功能",
"已有边界": "常规值",
"缺失边界": "边界值:XXX",
"建议用例名称": "测试XXX边界条件",
"优先级": "P2"
}
]
}
注意:
1. 必须输出合法的JSON格式
2. 只输出JSON对象,不要有其他说明文字
3. 优先级分为:P0(最高)、P1、P2、P3(最低)
4. 优先识别P0和P1级别的缺失
5. 考虑以下常见场景:
- 正常场景:基本功能验证
- 异常场景:错误处理、异常输入
- 边界条件:最大值、最小值、空值、null
- 并发场景:多用户同时操作
- 权限场景:不同角色权限验证
- 性能场景:大数据量、长时间操作
"""
return
prompt
def
_parse_gap_result
(
self
,
result
:
str
)
->
Dict
:
"""
解析AI返回的缺失结果
Args:
result: AI返回的文本
Returns:
缺失结果字典
"""
import
re
logger
.
debug
(
"尝试解析缺失结果JSON,内容长度:
%
d"
,
len
(
result
))
# 尝试直接解析
try
:
return
json
.
loads
(
result
)
except
json
.
JSONDecodeError
:
pass
# 尝试从代码块中提取
code_block_pattern
=
r'```(?:json)?\s*\n?([\s\S]*?)\n?```'
matches
=
re
.
findall
(
code_block_pattern
,
result
)
for
match
in
matches
:
try
:
gaps
=
json
.
loads
(
match
.
strip
())
logger
.
debug
(
"成功解析缺失检测结果"
)
return
gaps
except
:
continue
# 尝试提取花括号内容(对象)- 使用括号匹配处理嵌套
brace_count
=
0
start_idx
=
-
1
for
i
,
char
in
enumerate
(
result
):
if
char
==
'{'
:
if
brace_count
==
0
:
start_idx
=
i
brace_count
+=
1
elif
char
==
'}'
:
brace_count
-=
1
if
brace_count
==
0
and
start_idx
>=
0
:
try
:
json_str
=
result
[
start_idx
:
i
+
1
]
gaps
=
json
.
loads
(
json_str
)
logger
.
debug
(
"成功解析缺失检测结果"
)
return
gaps
except
:
break
logger
.
warning
(
"未找到有效的JSON对象格式"
)
logger
.
debug
(
"AI返回内容:
%
s"
,
result
[:
500
])
return
{
"功能点缺失"
:
[],
"场景覆盖不足"
:
[],
"边界条件不足"
:
[]}
def
check_coverage_target
(
self
,
coverage_rate
:
float
)
->
Dict
:
"""
检查是否达到覆盖率目标
Args:
coverage_rate: 当前覆盖率
Returns:
检查结果
"""
target_met
=
coverage_rate
>=
COVERAGE_TARGET
gap
=
COVERAGE_TARGET
-
coverage_rate
result
=
{
"current_coverage"
:
coverage_rate
,
"target_coverage"
:
COVERAGE_TARGET
,
"target_met"
:
target_met
,
"gap"
:
max
(
0
,
gap
),
"gap_percent"
:
max
(
0
,
gap
)
*
100
}
if
target_met
:
logger
.
info
(
"✅ 覆盖率达标:
%.1
f
%%
>=
%.1
f
%%
"
,
coverage_rate
*
100
,
COVERAGE_TARGET
*
100
)
else
:
logger
.
warning
(
"⚠️ 覆盖率未达标:
%.1
f
%%
<
%.1
f
%%
, 差距:
%.1
f
%%
"
,
coverage_rate
*
100
,
COVERAGE_TARGET
*
100
,
result
[
"gap_percent"
])
return
result
def
prioritize_gaps
(
self
,
gaps
:
Dict
)
->
Dict
:
"""
对缺失项进行优先级排序
Args:
gaps: 缺失检测结果
Returns:
按优先级排序的缺失项
"""
priority_order
=
{
"P0"
:
0
,
"P1"
:
1
,
"P2"
:
2
,
"P3"
:
3
}
def
sort_by_priority
(
item
):
priority
=
item
.
get
(
"优先级"
,
"P3"
)
return
priority_order
.
get
(
priority
,
4
)
result
=
{
"功能点缺失"
:
sorted
(
gaps
.
get
(
"功能点缺失"
,
[]),
key
=
sort_by_priority
),
"场景覆盖不足"
:
sorted
(
gaps
.
get
(
"场景覆盖不足"
,
[]),
key
=
sort_by_priority
),
"边界条件不足"
:
sorted
(
gaps
.
get
(
"边界条件不足"
,
[]),
key
=
sort_by_priority
)
}
# 统计各优先级数量
for
category
in
[
"功能点缺失"
,
"场景覆盖不足"
,
"边界条件不足"
]:
items
=
result
[
category
]
logger
.
info
(
"
%
s - P0:
%
d, P1:
%
d, P2:
%
d, P3:
%
d"
,
category
,
sum
(
1
for
i
in
items
if
i
.
get
(
"优先级"
)
==
"P0"
),
sum
(
1
for
i
in
items
if
i
.
get
(
"优先级"
)
==
"P1"
),
sum
(
1
for
i
in
items
if
i
.
get
(
"优先级"
)
==
"P2"
),
sum
(
1
for
i
in
items
if
i
.
get
(
"优先级"
)
==
"P3"
))
return
result
def
estimate_additional_cases
(
gaps
:
Dict
)
->
Dict
:
"""
估算需要新增的测试用例数量
Args:
gaps: 缺失检测结果
Returns:
估算结果
"""
function_gaps
=
len
(
gaps
.
get
(
"功能点缺失"
,
[]))
scenario_gaps
=
len
(
gaps
.
get
(
"场景覆盖不足"
,
[]))
boundary_gaps
=
len
(
gaps
.
get
(
"边界条件不足"
,
[]))
total
=
function_gaps
+
scenario_gaps
+
boundary_gaps
result
=
{
"功能点缺失"
:
function_gaps
,
"场景覆盖不足"
:
scenario_gaps
,
"边界条件不足"
:
boundary_gaps
,
"总计"
:
total
}
logger
.
info
(
"估算需新增测试用例:
%
d 条"
,
total
)
return
result
AuxiliaryTool/AIPerfectedTestCases/src/main.py
0 → 100644
浏览文件 @
ed7ea006
# -*- coding: utf-8 -*-
"""
AI完善测试用例工具 - 主入口模块
本模块是工具的主入口,负责协调整个处理流程
"""
import
os
import
sys
import
logging
from
pathlib
import
Path
# 先加载环境变量(优先使用.env文件)
try
:
from
dotenv
import
load_dotenv
# 使用 __file__ 获取当前文件所在目录的父目录(项目根目录)
CURRENT_DIR
=
Path
(
__file__
)
.
parent
env_file
=
CURRENT_DIR
.
parent
/
".env"
if
env_file
.
exists
():
load_dotenv
(
env_file
)
except
ImportError
:
pass
# python-dotenv 未安装,跳过
from
src.config
import
(
PROJECT_ROOT
,
REQUIREMENTS_DIR
,
PRD_DIR
,
TEST_CASE_FILE
,
OUTPUT_TEST_CASE
,
OUTPUT_REPORT
,
LOG_FILE
,
LOG_FORMAT
,
LOG_DATE_FORMAT
,
API_KEY_ENV
,
API_KEY_PREFIX
,
ensure_directories
,
COVERAGE_TARGET
,
get_ai_service_mode
)
from
src.document_reader
import
DocumentReader
,
validate_documents
from
src.test_case_analyzer
import
TestCaseAnalyzer
,
analyze_test_case_quality
from
src.gap_detector
import
GapDetector
,
estimate_additional_cases
from
src.case_generator
import
CaseGenerator
from
src.deduplicator
import
Deduplicator
from
src.report_generator
import
ReportGenerator
# 配置日志
def
setup_logging
(
log_level
:
str
=
"INFO"
)
->
None
:
"""
配置日志系统
Args:
log_level: 日志级别
"""
ensure_directories
()
level
=
getattr
(
logging
,
log_level
.
upper
(),
logging
.
INFO
)
logging
.
basicConfig
(
level
=
level
,
format
=
LOG_FORMAT
,
datefmt
=
LOG_DATE_FORMAT
,
handlers
=
[
logging
.
StreamHandler
(
sys
.
stdout
),
logging
.
FileHandler
(
LOG_FILE
,
encoding
=
"utf-8"
)
]
)
global
logger
logger
=
logging
.
getLogger
(
__name__
)
logger
.
info
(
"日志系统初始化完成,级别=
%
s"
,
log_level
)
logger
=
logging
.
getLogger
(
__name__
)
def
check_api_key
()
->
str
:
"""
检查API密钥
Returns:
API密钥字符串
Raises:
SystemExit: 如果API密钥不存在
"""
api_key
=
os
.
environ
.
get
(
API_KEY_ENV
)
if
not
api_key
:
logger
.
error
(
"未设置
%
s 环境变量"
,
API_KEY_ENV
)
logger
.
error
(
"请设置API密钥:"
)
logger
.
error
(
" Windows: set
%
s=your-key-here"
,
API_KEY_ENV
)
logger
.
error
(
" Linux/Mac: export
%
s=your-key-here"
,
API_KEY_ENV
)
sys
.
exit
(
1
)
# 隐藏API密钥的显示
masked_key
=
api_key
[:
8
]
+
"..."
if
len
(
api_key
)
>
8
else
"***"
logger
.
info
(
"API密钥已配置:
%
s"
,
masked_key
)
return
api_key
def
validate_inputs
()
->
None
:
"""
验证输入文件和目录
Raises:
SystemExit: 如果验证失败
"""
logger
.
info
(
"验证输入文件和目录..."
)
result
=
validate_documents
(
REQUIREMENTS_DIR
,
PRD_DIR
,
TEST_CASE_FILE
)
if
not
result
[
"valid"
]:
logger
.
error
(
"输入验证失败:"
)
for
error
in
result
[
"errors"
]:
logger
.
error
(
" -
%
s"
,
error
)
sys
.
exit
(
1
)
# 显示警告
if
result
[
"errors"
]:
logger
.
warning
(
"发现以下问题:"
)
for
error
in
result
[
"errors"
]:
logger
.
warning
(
" -
%
s"
,
error
)
logger
.
info
(
"输入验证通过"
)
def
main
(
use_cli
:
bool
=
False
)
->
int
:
"""
主函数
Args:
use_cli: 是否强制使用CLI模式
Returns:
退出码(0表示成功,非0表示失败)
"""
try
:
# 配置日志
setup_logging
()
ensure_directories
()
logger
.
info
(
"="
*
60
)
logger
.
info
(
"AI完善测试用例工具 启动"
)
logger
.
info
(
"版本: 1.0.0"
)
logger
.
info
(
"="
*
60
)
# 检查AI服务模式
api_key
=
None
service_mode
=
get_ai_service_mode
()
# 命令行参数优先级最高
if
use_cli
:
service_mode
=
"cli"
# 根据服务模式初始化
if
service_mode
==
"api"
:
# 强制使用API模式
api_key
=
os
.
environ
.
get
(
API_KEY_ENV
)
if
not
api_key
or
not
api_key
.
strip
():
logger
.
error
(
"API模式需要ANTHROPIC_API_KEY环境变量"
)
logger
.
error
(
"请设置API密钥:"
)
logger
.
error
(
" Windows: set ANTHROPIC_API_KEY=your-key-here"
)
logger
.
error
(
" Linux/Mac: export ANTHROPIC_API_KEY=your-key-here"
)
logger
.
error
(
"或在 .env 文件中配置: ANTHROPIC_API_KEY=your-key-here"
)
return
1
use_cli
=
False
masked_key
=
api_key
[:
8
]
+
"..."
if
len
(
api_key
)
>
8
else
"***"
logger
.
info
(
"使用Anthropic API模式(强制),密钥:
%
s"
,
masked_key
)
elif
service_mode
==
"cli"
:
# 强制使用CLI模式
use_cli
=
True
logger
.
info
(
"使用Claude CLI模式(强制)"
)
else
:
# auto模式:自动检测
api_key
=
os
.
environ
.
get
(
API_KEY_ENV
)
if
api_key
and
api_key
.
strip
():
logger
.
info
(
"检测到API密钥,将使用Anthropic API模式"
)
use_cli
=
False
else
:
logger
.
info
(
"未检测到有效的API密钥,将尝试使用Claude CLI模式"
)
logger
.
info
(
"提示:可以通过设置环境变量 AI_SERVICE_MODE=api 强制使用API模式"
)
use_cli
=
True
if
use_cli
:
logger
.
info
(
"使用Claude CLI模式"
)
else
:
masked_key
=
api_key
[:
8
]
+
"..."
if
len
(
api_key
)
>
8
else
"***"
logger
.
info
(
"使用Anthropic API模式,密钥:
%
s"
,
masked_key
)
# 验证输入
validate_inputs
()
# 1. 读取文档
logger
.
info
(
""
)
logger
.
info
(
"【步骤1】读取文档"
)
logger
.
info
(
"-"
*
60
)
reader
=
DocumentReader
()
docs
=
reader
.
read_all_documents
(
REQUIREMENTS_DIR
,
PRD_DIR
,
TEST_CASE_FILE
)
requirements
=
docs
[
"requirements"
]
prds
=
docs
[
"prds"
]
test_cases
=
docs
[
"test_cases"
]
# 2. 分析测试用例质量
logger
.
info
(
""
)
logger
.
info
(
"【步骤2】分析测试用例质量"
)
logger
.
info
(
"-"
*
60
)
quality_report
=
analyze_test_case_quality
(
test_cases
)
# 3. 提取功能点
logger
.
info
(
""
)
logger
.
info
(
"【步骤3】提取功能点"
)
logger
.
info
(
"-"
*
60
)
analyzer
=
TestCaseAnalyzer
(
api_key
=
api_key
,
use_cli
=
use_cli
)
function_points
=
analyzer
.
extract_function_points
(
requirements
,
prds
)
if
not
function_points
:
logger
.
warning
(
"未提取到功能点,请检查需求文档内容"
)
# 继续执行,但使用空列表
# 4. 分析覆盖率
logger
.
info
(
""
)
logger
.
info
(
"【步骤4】分析覆盖率"
)
logger
.
info
(
"-"
*
60
)
coverage
=
analyzer
.
analyze_coverage
(
function_points
,
test_cases
)
# 5. 检测缺失
logger
.
info
(
""
)
logger
.
info
(
"【步骤5】检测缺失"
)
logger
.
info
(
"-"
*
60
)
detector
=
GapDetector
(
api_key
=
api_key
,
use_cli
=
use_cli
)
gaps
=
detector
.
detect_gaps
(
function_points
,
test_cases
,
requirements
,
prds
)
# 检查覆盖率目标
coverage_check
=
detector
.
check_coverage_target
(
coverage
.
get
(
"coverage_rate"
,
0
))
# 估算新增用例数量
estimate
=
estimate_additional_cases
(
gaps
)
# 6. 生成新用例
logger
.
info
(
""
)
logger
.
info
(
"【步骤6】生成新测试用例"
)
logger
.
info
(
"-"
*
60
)
generator
=
CaseGenerator
(
api_key
=
api_key
,
use_cli
=
use_cli
)
new_cases
=
generator
.
generate_cases
(
gaps
,
requirements
,
prds
,
test_cases
)
# 7. 去重
logger
.
info
(
""
)
logger
.
info
(
"【步骤7】去重处理"
)
logger
.
info
(
"-"
*
60
)
deduplicator
=
Deduplicator
()
dedup_result
=
deduplicator
.
deduplicate
(
test_cases
,
new_cases
)
# 8. 保存完善后的测试用例
logger
.
info
(
""
)
logger
.
info
(
"【步骤8】保存完善后的测试用例"
)
logger
.
info
(
"-"
*
60
)
save_enhanced_test_cases
(
test_cases
,
dedup_result
[
"unique_cases"
],
OUTPUT_TEST_CASE
)
# 9. 生成差异性报告
logger
.
info
(
""
)
logger
.
info
(
"【步骤9】生成差异性报告"
)
logger
.
info
(
"-"
*
60
)
reporter
=
ReportGenerator
()
reporter
.
generate_report
(
gaps
,
dedup_result
,
coverage
,
OUTPUT_REPORT
)
# 10. 输出总结
logger
.
info
(
""
)
logger
.
info
(
"="
*
60
)
logger
.
info
(
"处理完成!"
)
logger
.
info
(
"="
*
60
)
logger
.
info
(
"【输入】"
)
logger
.
info
(
" 需求文档:
%
d 份"
,
len
(
requirements
))
logger
.
info
(
" PRD文档:
%
d 份"
,
len
(
prds
))
logger
.
info
(
" 原始用例:
%
d 条"
,
len
(
test_cases
))
logger
.
info
(
""
)
logger
.
info
(
"【输出】"
)
logger
.
info
(
" 完善后的测试用例:
%
s"
,
OUTPUT_TEST_CASE
)
logger
.
info
(
" 差异性报告:
%
s"
,
OUTPUT_REPORT
)
logger
.
info
(
""
)
logger
.
info
(
"【统计】"
)
logger
.
info
(
" 覆盖率:
%.1
f
%% -
>
%.1
f
%%
(预计)"
,
coverage
.
get
(
"coverage_rate"
,
0
)
*
100
,
min
(
100
,
coverage
.
get
(
"coverage_rate"
,
0
)
*
100
+
20
))
logger
.
info
(
" 新增用例:
%
d 条(去重后)"
,
dedup_result
[
"summary"
][
"unique_cases"
])
logger
.
info
(
" 功能点缺失:
%
d 项"
,
len
(
gaps
.
get
(
"功能点缺失"
,
[])))
logger
.
info
(
" 场景缺失:
%
d 项"
,
len
(
gaps
.
get
(
"场景覆盖不足"
,
[])))
logger
.
info
(
" 边界缺失:
%
d 项"
,
len
(
gaps
.
get
(
"边界条件不足"
,
[])))
logger
.
info
(
"="
*
60
)
return
0
except
KeyboardInterrupt
:
logger
.
info
(
""
)
logger
.
info
(
"用户中断操作"
)
return
130
except
FileNotFoundError
as
e
:
logger
.
error
(
"文件未找到:
%
s"
,
e
)
return
1
except
PermissionError
as
e
:
logger
.
error
(
"权限错误:
%
s"
,
e
)
logger
.
error
(
"请检查文件是否被其他程序占用"
)
return
1
except
Exception
as
e
:
logger
.
error
(
"处理失败:
%
s"
,
e
)
import
traceback
logger
.
debug
(
traceback
.
format_exc
())
return
1
def
save_enhanced_test_cases
(
existing_cases
:
list
,
new_cases
:
list
,
output_path
:
Path
)
->
None
:
"""
保存完善后的测试用例
Args:
existing_cases: 现有测试用例列表
new_cases: 新增测试用例列表
output_path: 输出文件路径
"""
import
openpyxl
from
openpyxl.styles
import
Font
,
PatternFill
from
src.config
import
TEST_CASE_COLUMNS
logger
.
info
(
"正在保存完善后的测试用例..."
)
# 加载原始文件
wb
=
openpyxl
.
load_workbook
(
TEST_CASE_FILE
)
ws
=
wb
.
active
# 获取下一个序列号
max_seq
=
0
for
row
in
ws
.
iter_rows
(
min_row
=
2
,
max_row
=
ws
.
max_row
,
values_only
=
True
):
seq
=
row
[
0
]
# 假设序列号是第一列
if
isinstance
(
seq
,
int
):
max_seq
=
max
(
max_seq
,
seq
)
# 定义新用例的样式(浅蓝色背景)
new_case_fill
=
PatternFill
(
start_color
=
"E6F2FF"
,
end_color
=
"E6F2FF"
,
fill_type
=
"solid"
)
new_case_font
=
Font
(
bold
=
True
)
# 追加新用例
for
i
,
case
in
enumerate
(
new_cases
):
max_seq
+=
1
row_data
=
[
case
.
get
(
col
,
""
)
for
col
in
TEST_CASE_COLUMNS
]
row_data
[
0
]
=
max_seq
# 更新序列号
ws
.
append
(
row_data
)
# 为新添加的行设置样式
new_row
=
ws
.
max_row
for
cell
in
ws
[
new_row
]:
cell
.
fill
=
new_case_fill
cell
.
font
=
new_case_font
# 保存文件
output_path
.
parent
.
mkdir
(
parents
=
True
,
exist_ok
=
True
)
wb
.
save
(
str
(
output_path
))
logger
.
info
(
"完善后的测试用例已保存:
%
s"
,
output_path
)
logger
.
info
(
" 原有用例:
%
d 条"
,
len
(
existing_cases
))
logger
.
info
(
" 新增用例:
%
d 条"
,
len
(
new_cases
))
logger
.
info
(
" 总计:
%
d 条"
,
len
(
existing_cases
)
+
len
(
new_cases
))
if
__name__
==
"__main__"
:
sys
.
exit
(
main
())
AuxiliaryTool/AIPerfectedTestCases/src/report_generator.py
0 → 100644
浏览文件 @
ed7ea006
# -*- coding: utf-8 -*-
"""
报告生成模块
本模块负责生成差异性报告
"""
from
pathlib
import
Path
from
typing
import
List
,
Dict
from
logging
import
getLogger
from
datetime
import
datetime
try
:
from
docx
import
Document
from
docx.shared
import
Pt
,
RGBColor
,
Inches
from
docx.enum.text
import
WD_ALIGN_PARAGRAPH
except
ImportError
:
print
(
"警告: 未安装python-docx库,请运行: pip install python-docx"
)
raise
logger
=
getLogger
(
__name__
)
class
ReportGenerator
:
"""
差异性报告生成器
主要功能:
1. 生成Word格式的差异性报告
2. 包含功能点缺失、场景覆盖不足、边界条件不足
3. 包含去重处理结果和统计信息
"""
def
__init__
(
self
):
"""初始化报告生成器"""
logger
.
info
(
"报告生成器初始化完成"
)
def
generate_report
(
self
,
gaps
:
Dict
,
dedup_result
:
Dict
,
coverage
:
Dict
,
output_path
:
Path
)
->
None
:
"""
生成差异性报告
Args:
gaps: 缺失检测结果
dedup_result: 去重结果
coverage: 覆盖率分析结果
output_path: 输出文件路径
"""
logger
.
info
(
"开始生成差异性报告:
%
s"
,
output_path
)
doc
=
Document
()
# 标题
self
.
_add_title
(
doc
)
# 生成时间
self
.
_add_generation_time
(
doc
)
# 1. 功能点缺失
self
.
_add_missing_functions_section
(
doc
,
gaps
.
get
(
"功能点缺失"
,
[]))
# 2. 场景覆盖不足
self
.
_add_scenarios_section
(
doc
,
gaps
.
get
(
"场景覆盖不足"
,
[]))
# 3. 边界条件不足
self
.
_add_boundaries_section
(
doc
,
gaps
.
get
(
"边界条件不足"
,
[]))
# 4. 去重处理结果
self
.
_add_deduplication_section
(
doc
,
dedup_result
)
# 5. 用例数量增长统计
self
.
_add_statistics_section
(
doc
,
coverage
,
dedup_result
)
# 6. 覆盖率百分比统计
self
.
_add_coverage_section
(
doc
,
coverage
)
# 7. 总结和建议
self
.
_add_summary_section
(
doc
,
coverage
,
dedup_result
)
# 确保输出目录存在
output_path
.
parent
.
mkdir
(
parents
=
True
,
exist_ok
=
True
)
# 保存文档
doc
.
save
(
str
(
output_path
))
logger
.
info
(
"报告生成完成:
%
s"
,
output_path
)
def
_add_title
(
self
,
doc
:
Document
)
->
None
:
"""添加报告标题"""
title
=
doc
.
add_heading
(
"测试用例完善差异性报告"
,
0
)
title
.
alignment
=
WD_ALIGN_PARAGRAPH
.
CENTER
def
_add_generation_time
(
self
,
doc
:
Document
)
->
None
:
"""添加生成时间"""
p
=
doc
.
add_paragraph
()
p
.
alignment
=
WD_ALIGN_PARAGRAPH
.
RIGHT
run
=
p
.
add_run
(
f
"生成时间:{datetime.now().strftime('
%
Y-
%
m-
%
d
%
H:
%
M:
%
S')}"
)
run
.
font
.
size
=
Pt
(
10
)
run
.
font
.
color
.
rgb
=
RGBColor
(
128
,
128
,
128
)
doc
.
add_paragraph
()
# 空行
def
_add_missing_functions_section
(
self
,
doc
:
Document
,
gaps
:
List
[
Dict
])
->
None
:
"""添加功能点缺失部分"""
doc
.
add_heading
(
"一、功能点缺失的测试用例"
,
level
=
1
)
if
not
gaps
:
doc
.
add_paragraph
(
"✅ 无功能点缺失"
)
.
italic
=
True
else
:
# 统计各优先级数量
p0_count
=
sum
(
1
for
g
in
gaps
if
g
.
get
(
"优先级"
)
==
"P0"
)
p1_count
=
sum
(
1
for
g
in
gaps
if
g
.
get
(
"优先级"
)
==
"P1"
)
p2_count
=
sum
(
1
for
g
in
gaps
if
g
.
get
(
"优先级"
)
==
"P2"
)
p3_count
=
sum
(
1
for
g
in
gaps
if
g
.
get
(
"优先级"
)
==
"P3"
)
p
=
doc
.
add_paragraph
()
p
.
add_run
(
f
"共 {len(gaps)} 项缺失:"
)
p
.
add_run
(
f
" P0: {p0_count} 项"
)
.
font
.
color
.
rgb
=
RGBColor
(
220
,
20
,
60
)
p
.
add_run
(
f
",P1: {p1_count} 项"
)
.
font
.
color
.
rgb
=
RGBColor
(
255
,
140
,
0
)
p
.
add_run
(
f
",P2: {p2_count} 项,P3: {p3_count} 项"
)
doc
.
add_paragraph
()
# 表格
table
=
doc
.
add_table
(
rows
=
1
,
cols
=
4
)
table
.
style
=
'Light Grid Accent 1'
# 表头
headers
=
table
.
rows
[
0
]
.
cells
headers
[
0
]
.
text
=
"序号"
headers
[
1
]
.
text
=
"功能模块"
headers
[
2
]
.
text
=
"功能描述"
headers
[
3
]
.
text
=
"优先级"
# 设置表头样式
for
cell
in
headers
:
for
paragraph
in
cell
.
paragraphs
:
paragraph
.
runs
[
0
]
.
font
.
bold
=
True
# 数据行
for
i
,
gap
in
enumerate
(
gaps
,
1
):
row
=
table
.
add_row
()
.
cells
row
[
0
]
.
text
=
str
(
i
)
row
[
1
]
.
text
=
gap
.
get
(
"功能模块"
,
""
)
row
[
2
]
.
text
=
gap
.
get
(
"功能描述"
,
""
)
row
[
3
]
.
text
=
gap
.
get
(
"优先级"
,
"P2"
)
# 根据优先级设置颜色
priority
=
gap
.
get
(
"优先级"
,
"P2"
)
if
priority
==
"P0"
:
row
[
3
]
.
paragraphs
[
0
]
.
runs
[
0
]
.
font
.
color
.
rgb
=
RGBColor
(
220
,
20
,
60
)
elif
priority
==
"P1"
:
row
[
3
]
.
paragraphs
[
0
]
.
runs
[
0
]
.
font
.
color
.
rgb
=
RGBColor
(
255
,
140
,
0
)
def
_add_scenarios_section
(
self
,
doc
:
Document
,
gaps
:
List
[
Dict
])
->
None
:
"""添加场景覆盖不足部分"""
doc
.
add_heading
(
"二、场景覆盖不足的测试用例"
,
level
=
1
)
if
not
gaps
:
doc
.
add_paragraph
(
"✅ 无场景覆盖不足"
)
.
italic
=
True
else
:
doc
.
add_paragraph
(
f
"共 {len(gaps)} 项场景覆盖不足:"
)
doc
.
add_paragraph
()
# 表格
table
=
doc
.
add_table
(
rows
=
1
,
cols
=
4
)
table
.
style
=
'Light Grid Accent 1'
# 表头
headers
=
table
.
rows
[
0
]
.
cells
headers
[
0
]
.
text
=
"序号"
headers
[
1
]
.
text
=
"功能描述"
headers
[
2
]
.
text
=
"缺失场景"
headers
[
3
]
.
text
=
"优先级"
for
cell
in
headers
:
for
paragraph
in
cell
.
paragraphs
:
paragraph
.
runs
[
0
]
.
font
.
bold
=
True
# 数据行
for
i
,
gap
in
enumerate
(
gaps
,
1
):
row
=
table
.
add_row
()
.
cells
row
[
0
]
.
text
=
str
(
i
)
row
[
1
]
.
text
=
gap
.
get
(
"功能描述"
,
""
)
row
[
2
]
.
text
=
gap
.
get
(
"缺失场景"
,
""
)
row
[
3
]
.
text
=
gap
.
get
(
"优先级"
,
"P2"
)
def
_add_boundaries_section
(
self
,
doc
:
Document
,
gaps
:
List
[
Dict
])
->
None
:
"""添加边界条件不足部分"""
doc
.
add_heading
(
"三、边界条件不足的测试用例"
,
level
=
1
)
if
not
gaps
:
doc
.
add_paragraph
(
"✅ 无边界条件不足"
)
.
italic
=
True
else
:
doc
.
add_paragraph
(
f
"共 {len(gaps)} 项边界条件不足:"
)
doc
.
add_paragraph
()
# 表格
table
=
doc
.
add_table
(
rows
=
1
,
cols
=
4
)
table
.
style
=
'Light Grid Accent 1'
# 表头
headers
=
table
.
rows
[
0
]
.
cells
headers
[
0
]
.
text
=
"序号"
headers
[
1
]
.
text
=
"功能描述"
headers
[
2
]
.
text
=
"缺失边界"
headers
[
3
]
.
text
=
"优先级"
for
cell
in
headers
:
for
paragraph
in
cell
.
paragraphs
:
paragraph
.
runs
[
0
]
.
font
.
bold
=
True
# 数据行
for
i
,
gap
in
enumerate
(
gaps
,
1
):
row
=
table
.
add_row
()
.
cells
row
[
0
]
.
text
=
str
(
i
)
row
[
1
]
.
text
=
gap
.
get
(
"功能描述"
,
""
)
row
[
2
]
.
text
=
gap
.
get
(
"缺失边界"
,
""
)
row
[
3
]
.
text
=
gap
.
get
(
"优先级"
,
"P2"
)
def
_add_deduplication_section
(
self
,
doc
:
Document
,
dedup_result
:
Dict
)
->
None
:
"""添加去重处理结果部分"""
doc
.
add_heading
(
"四、去重处理结果"
,
level
=
1
)
summary
=
dedup_result
.
get
(
"summary"
,
{})
doc
.
add_paragraph
(
f
"新增测试用例:{summary.get('new_cases', 0)} 条"
)
doc
.
add_paragraph
(
f
"去重后保留:{summary.get('unique_cases', 0)} 条"
)
doc
.
add_paragraph
(
f
"与现有用例重复:{summary.get('duplicate_with_existing', 0)} 条"
)
doc
.
add_paragraph
(
f
"新用例内部重复:{summary.get('duplicate_within_new', 0)} 条"
)
# 去重率
dedup_rate
=
summary
.
get
(
"deduplication_rate"
,
0
)
*
100
p
=
doc
.
add_paragraph
()
p
.
add_run
(
f
"去重率:{dedup_rate:.1f}
%
"
)
p
.
runs
[
0
]
.
font
.
bold
=
True
p
.
runs
[
0
]
.
font
.
size
=
Pt
(
12
)
def
_add_statistics_section
(
self
,
doc
:
Document
,
coverage
:
Dict
,
dedup_result
:
Dict
)
->
None
:
"""添加用例数量增长统计部分"""
doc
.
add_heading
(
"五、用例数量增长统计"
,
level
=
1
)
# 表格
table
=
doc
.
add_table
(
rows
=
4
,
cols
=
2
)
table
.
style
=
'Light Grid Accent 1'
# 表头
headers
=
table
.
rows
[
0
]
.
cells
headers
[
0
]
.
text
=
"统计项"
headers
[
1
]
.
text
=
"数量"
for
cell
in
headers
:
for
paragraph
in
cell
.
paragraphs
:
paragraph
.
runs
[
0
]
.
font
.
bold
=
True
# 数据行
table
.
rows
[
1
]
.
cells
[
0
]
.
text
=
"原有测试用例数"
table
.
rows
[
1
]
.
cells
[
1
]
.
text
=
str
(
coverage
.
get
(
"covered_functions"
,
0
))
table
.
rows
[
2
]
.
cells
[
0
]
.
text
=
"新增测试用例数(去重后)"
table
.
rows
[
2
]
.
cells
[
1
]
.
text
=
str
(
dedup_result
.
get
(
"summary"
,
{})
.
get
(
"unique_cases"
,
0
))
total
=
coverage
.
get
(
"covered_functions"
,
0
)
+
dedup_result
.
get
(
"summary"
,
{})
.
get
(
"unique_cases"
,
0
)
table
.
rows
[
3
]
.
cells
[
0
]
.
text
=
"完善后测试用例总数"
table
.
rows
[
3
]
.
cells
[
1
]
.
text
=
str
(
total
)
# 高亮最后一行
for
cell
in
table
.
rows
[
3
]
.
cells
:
for
paragraph
in
cell
.
paragraphs
:
paragraph
.
runs
[
0
]
.
font
.
bold
=
True
paragraph
.
runs
[
0
]
.
font
.
color
.
rgb
=
RGBColor
(
0
,
100
,
0
)
def
_add_coverage_section
(
self
,
doc
:
Document
,
coverage
:
Dict
)
->
None
:
"""添加覆盖率百分比统计部分"""
doc
.
add_heading
(
"六、覆盖率百分比统计"
,
level
=
1
)
coverage_rate
=
coverage
.
get
(
"coverage_rate"
,
0
)
*
100
target_coverage
=
90
# 当前覆盖率
p
=
doc
.
add_paragraph
()
p
.
add_run
(
"当前覆盖率:"
)
.
bold
=
True
p
.
add_run
(
f
"{coverage_rate:.1f}
%
"
)
# 目标覆盖率
p
=
doc
.
add_paragraph
()
p
.
add_run
(
"目标覆盖率:"
)
.
bold
=
True
p
.
add_run
(
f
"{target_coverage}
%
"
)
doc
.
add_paragraph
()
# 状态判断
if
coverage_rate
>=
target_coverage
:
p
=
doc
.
add_paragraph
()
run
=
p
.
add_run
(
"✅ 覆盖率达标!"
)
run
.
font
.
bold
=
True
run
.
font
.
size
=
Pt
(
14
)
run
.
font
.
color
.
rgb
=
RGBColor
(
0
,
128
,
0
)
else
:
gap
=
target_coverage
-
coverage_rate
p
=
doc
.
add_paragraph
()
run
=
p
.
add_run
(
f
"⚠️ 距离目标还差 {gap:.1f}
%
"
)
run
.
font
.
bold
=
True
run
.
font
.
size
=
Pt
(
14
)
run
.
font
.
color
.
rgb
=
RGBColor
(
255
,
140
,
0
)
def
_add_summary_section
(
self
,
doc
:
Document
,
coverage
:
Dict
,
dedup_result
:
Dict
)
->
None
:
"""添加总结和建议部分"""
doc
.
add_heading
(
"七、总结和建议"
,
level
=
1
)
# 总体情况
coverage_rate
=
coverage
.
get
(
"coverage_rate"
,
0
)
*
100
new_cases
=
dedup_result
.
get
(
"summary"
,
{})
.
get
(
"unique_cases"
,
0
)
doc
.
add_paragraph
(
"【总体情况】"
)
doc
.
add_paragraph
(
f
"• 当前测试用例覆盖率:{coverage_rate:.1f}
%
"
)
doc
.
add_paragraph
(
f
"• 计划新增测试用例:{new_cases} 条"
)
doc
.
add_paragraph
()
# 建议
doc
.
add_paragraph
(
"【建议】"
)
if
coverage_rate
<
90
:
doc
.
add_paragraph
(
f
"• 当前覆盖率未达到目标(90
%
),建议优先补充P0和P1级别的测试用例"
)
if
new_cases
>
50
:
doc
.
add_paragraph
(
f
"• 新增测试用例较多({new_cases}条),建议分批执行和验证"
)
doc
.
add_paragraph
(
"• 建议对AI生成的测试用例进行人工审核,确保用例质量"
)
doc
.
add_paragraph
(
"• 建议在执行新增用例后,再次评估覆盖率"
)
doc
.
add_paragraph
()
# 注意事项
doc
.
add_paragraph
(
"【注意事项】"
)
doc
.
add_paragraph
(
"• 本报告由AI自动生成,仅供参考"
)
doc
.
add_paragraph
(
"• 具体的测试用例内容请在完善版本的Excel文件中查看"
)
def
generate_simple_report
(
gaps
:
Dict
,
dedup_result
:
Dict
,
coverage
:
Dict
,
output_path
:
Path
)
->
None
:
"""
生成简化的报告(仅统计信息)
Args:
gaps: 缺失检测结果
dedup_result: 去重结果
coverage: 覆盖率分析结果
output_path: 输出文件路径
"""
doc
=
Document
()
doc
.
add_heading
(
"测试用例完善统计报告"
,
0
)
# 覆盖率
coverage_rate
=
coverage
.
get
(
"coverage_rate"
,
0
)
*
100
doc
.
add_paragraph
(
f
"当前覆盖率:{coverage_rate:.1f}
%
"
)
# 缺失统计
doc
.
add_paragraph
(
f
"功能点缺失:{len(gaps.get('功能点缺失', []))} 项"
)
doc
.
add_paragraph
(
f
"场景覆盖不足:{len(gaps.get('场景覆盖不足', []))} 项"
)
doc
.
add_paragraph
(
f
"边界条件不足:{len(gaps.get('边界条件不足', []))} 项"
)
# 新增用例
new_cases
=
dedup_result
.
get
(
"summary"
,
{})
.
get
(
"unique_cases"
,
0
)
doc
.
add_paragraph
(
f
"新增测试用例(去重后):{new_cases} 条"
)
output_path
.
parent
.
mkdir
(
parents
=
True
,
exist_ok
=
True
)
doc
.
save
(
str
(
output_path
))
AuxiliaryTool/AIPerfectedTestCases/src/test_case_analyzer.py
0 → 100644
浏览文件 @
ed7ea006
# -*- coding: utf-8 -*-
"""
测试用例分析模块
本模块负责使用AI分析需求文档和PRD文档,提取功能点列表
"""
from
typing
import
List
,
Dict
,
Optional
,
Any
from
logging
import
getLogger
import
json
import
re
import
os
try
:
import
anthropic
except
ImportError
:
anthropic
=
None
from
src.config
import
(
AI_MODEL
,
AI_MAX_TOKENS
,
AI_TEMPERATURE
,
API_KEY_ENV
,
API_KEY_PREFIX
,
get_api_base_url
)
logger
=
getLogger
(
__name__
)
class
AIClientInterface
:
"""AI客户端接口(抽象类)"""
def
ask_json
(
self
,
prompt
:
str
,
model
:
Optional
[
str
]
=
None
)
->
Any
:
"""发送提示词并返回JSON结果"""
raise
NotImplementedError
class
AnthropicAPIClient
(
AIClientInterface
):
"""Anthropic API客户端"""
def
__init__
(
self
,
api_key
:
str
,
base_url
:
Optional
[
str
]
=
None
):
if
anthropic
is
None
:
raise
ImportError
(
"未安装anthropic库,请运行: pip install anthropic"
)
kwargs
=
{
"api_key"
:
api_key
}
if
base_url
:
kwargs
[
"base_url"
]
=
base_url
logger
.
info
(
"使用自定义API endpoint:
%
s"
,
base_url
)
self
.
client
=
anthropic
.
Anthropic
(
**
kwargs
)
def
ask_json
(
self
,
prompt
:
str
,
model
:
Optional
[
str
]
=
None
)
->
Any
:
"""调用Anthropic API"""
model
=
model
or
AI_MODEL
response
=
self
.
client
.
messages
.
create
(
model
=
model
,
max_tokens
=
AI_MAX_TOKENS
,
temperature
=
AI_TEMPERATURE
,
messages
=
[{
"role"
:
"user"
,
"content"
:
prompt
}]
)
return
response
.
content
[
0
]
.
text
class
ClaudeCLIClient
(
AIClientInterface
):
"""Claude CLI客户端"""
def
__init__
(
self
):
from
src.claude_cli_adapter
import
ClaudeCLIAdapter
self
.
adapter
=
ClaudeCLIAdapter
()
if
not
self
.
adapter
.
check_available
():
raise
RuntimeError
(
"Claude CLI不可用"
)
def
ask_json
(
self
,
prompt
:
str
,
model
:
Optional
[
str
]
=
None
)
->
Any
:
"""调用Claude CLI"""
# CLI适配器会自动处理JSON解析
return
self
.
adapter
.
ask_json
(
prompt
,
model
)
class
TestCaseAnalyzer
:
"""
测试用例分析器
主要功能:
1. 使用AI提取功能点列表
2. 分析测试用例覆盖情况
支持两种AI服务模式:
- Anthropic API:使用官方API密钥
- Claude CLI:使用本地CLI工具
"""
def
__init__
(
self
,
api_key
:
Optional
[
str
]
=
None
,
use_cli
:
bool
=
False
):
"""
初始化分析器
Args:
api_key: Anthropic API密钥(可选)
use_cli: 是否强制使用CLI模式(可选)
"""
# 获取API base URL
base_url
=
get_api_base_url
()
# 决定使用哪种模式
if
use_cli
:
logger
.
info
(
"强制使用Claude CLI模式"
)
self
.
ai_client
=
ClaudeCLIClient
()
self
.
mode
=
"cli"
elif
api_key
and
api_key
.
strip
():
logger
.
info
(
"使用Anthropic API模式"
)
self
.
ai_client
=
AnthropicAPIClient
(
api_key
,
base_url
)
self
.
mode
=
"api"
else
:
# 尝试自动检测
api_key_from_env
=
os
.
environ
.
get
(
API_KEY_ENV
,
""
)
if
api_key_from_env
and
api_key_from_env
.
strip
():
logger
.
info
(
"检测到API密钥,使用Anthropic API模式"
)
self
.
ai_client
=
AnthropicAPIClient
(
api_key_from_env
,
base_url
)
self
.
mode
=
"api"
else
:
logger
.
info
(
"未检测到API密钥,尝试使用Claude CLI模式"
)
try
:
self
.
ai_client
=
ClaudeCLIClient
()
self
.
mode
=
"cli"
except
RuntimeError
:
raise
RuntimeError
(
"无法初始化AI服务:
\n
"
"1. 未设置Anthropic API密钥
\n
"
"2. Claude CLI工具不可用
\n
"
"请至少配置其中一种方式"
)
logger
.
info
(
"测试用例分析器初始化完成,模式:
%
s"
,
self
.
mode
)
def
extract_function_points
(
self
,
requirements
:
List
[
Dict
],
prds
:
List
[
Dict
])
->
List
[
Dict
]:
"""
使用AI提取功能点列表
Args:
requirements: 需求文档列表
prds: PRD文档列表
Returns:
功能点列表,每项包含:
- 功能模块: 模块名称
- 功能编号: 功能编号
- 功能描述: 功能描述
- 子功能: 子功能列表(可选)
"""
logger
.
info
(
"开始提取功能点..."
)
# 检查是否有需求文档
has_requirements
=
len
(
requirements
)
>
0
if
has_requirements
:
logger
.
info
(
"使用需求文档和PRD文档提取功能点"
)
prompt
=
self
.
_build_extract_prompt
(
requirements
,
prds
)
else
:
logger
.
warning
(
"没有需求文档,仅使用PRD文档提取功能点"
)
prompt
=
self
.
_build_prd_only_prompt
(
prds
)
try
:
# 使用AI客户端发送请求
if
self
.
mode
==
"api"
:
# API模式:需要解析返回的文本
result_text
=
self
.
ai_client
.
ask_json
(
prompt
,
AI_MODEL
)
function_points
=
self
.
_parse_function_points
(
result_text
)
else
:
# CLI模式:adapter直接返回解析后的JSON
function_points
=
self
.
ai_client
.
ask_json
(
prompt
,
AI_MODEL
)
if
isinstance
(
function_points
,
dict
):
function_points
=
[
function_points
]
if
not
isinstance
(
function_points
,
list
):
function_points
=
[]
logger
.
info
(
"功能点提取完成,共
%
d 个"
,
len
(
function_points
))
return
function_points
except
Exception
as
e
:
logger
.
error
(
"提取功能点失败:
%
s"
,
e
)
logger
.
debug
(
"错误详情"
,
exc_info
=
True
)
return
[]
def
_build_prd_only_prompt
(
self
,
prds
:
List
[
Dict
])
->
str
:
"""
构建仅从PRD提取功能点的提示词
Args:
prds: PRD文档列表
Returns:
提示词字符串
"""
prompt
=
"""你是一个专业的测试用例分析专家。请分析以下开发PRD文档,提取完整的功能点列表。
【要求】
1. 按功能模块分类
2. 每个功能点包含:功能模块、功能编号、功能描述
3. 识别所有子功能
4. 输出格式必须是JSON数组
【开发PRD文档】
"""
# 添加PRD文档内容(限制长度)
for
prd
in
prds
:
prompt
+=
f
"
\n
--- {prd['filename']} ---
\n
"
content
=
prd
[
'content'
]
# 限制每个文档最多5000字符
if
len
(
content
)
>
5000
:
content
=
content
[:
5000
]
+
"
\n
...(内容已截断)"
prompt
+=
content
+
"
\n
"
prompt
+=
"""
\n
请输出JSON格式的功能点列表,格式如下:
[
{
"功能模块": "模块名称",
"功能编号": "F-001",
"功能描述": "功能描述",
"子功能": ["子功能1", "子功能2"]
}
]
注意:
1. 必须输出合法的JSON格式
2. 功能编号格式:模块简称-序号,如 AUTH-001, USER-002
3. 如果没有子功能,可以省略"子功能"字段或设为空数组
4. 只输出JSON数组,不要有其他说明文字
"""
return
prompt
def
_build_extract_prompt
(
self
,
requirements
:
List
[
Dict
],
prds
:
List
[
Dict
])
->
str
:
"""
构建功能点提取提示词
Args:
requirements: 需求文档列表
prds: PRD文档列表
Returns:
提示词字符串
"""
prompt
=
"""你是一个专业的测试用例分析专家。请分析以下需求文档和PRD文档,提取完整的功能点列表。
【要求】
1. 按功能模块分类
2. 每个功能点包含:功能模块、功能编号、功能描述
3. 识别所有子功能
4. 输出格式必须是JSON数组
【需求文档】
"""
# 添加需求文档内容(限制长度)
for
req
in
requirements
:
prompt
+=
f
"
\n
--- {req['filename']} ---
\n
"
content
=
req
[
'content'
]
# 限制每个文档最多3000字符
if
len
(
content
)
>
3000
:
content
=
content
[:
3000
]
+
"
\n
...(内容已截断)"
prompt
+=
content
+
"
\n
"
prompt
+=
"
\n
【开发PRD文档】
\n
"
# 添加PRD文档内容(限制长度)
for
prd
in
prds
:
prompt
+=
f
"
\n
--- {prd['filename']} ---
\n
"
content
=
prd
[
'content'
]
# 限制每个文档最多3000字符
if
len
(
content
)
>
3000
:
content
=
content
[:
3000
]
+
"
\n
...(内容已截断)"
prompt
+=
content
+
"
\n
"
prompt
+=
"""
\n
请输出JSON格式的功能点列表,格式如下:
[
{
"功能模块": "模块名称",
"功能编号": "F-001",
"功能描述": "功能描述",
"子功能": ["子功能1", "子功能2"]
},
{
"功能模块": "模块名称",
"功能编号": "F-002",
"功能描述": "功能描述",
"子功能": ["子功能1", "子功能2", "子功能3"]
}
]
注意:
1. 必须输出合法的JSON格式
2. 功能编号格式:模块简称-序号,如 AUTH-001, USER-002
3. 如果没有子功能,可以省略"子功能"字段或设为空数组
4. 只输出JSON数组,不要有其他说明文字
"""
return
prompt
def
_parse_function_points
(
self
,
result
:
str
)
->
List
[
Dict
]:
"""
解析AI返回的功能点
Args:
result: AI返回的文本
Returns:
功能点列表
"""
import
re
logger
.
debug
(
"尝试解析功能点JSON,内容长度:
%
d"
,
len
(
result
))
# 尝试直接解析
try
:
return
json
.
loads
(
result
)
except
json
.
JSONDecodeError
:
pass
# 尝试从代码块中提取
code_block_pattern
=
r'```(?:json)?\s*\n?([\s\S]*?)\n?```'
matches
=
re
.
findall
(
code_block_pattern
,
result
)
for
match
in
matches
:
try
:
return
json
.
loads
(
match
.
strip
())
except
:
continue
# 尝试提取方括号内容(数组)- 使用括号匹配处理嵌套
bracket_count
=
0
start_idx
=
-
1
for
i
,
char
in
enumerate
(
result
):
if
char
==
'['
:
if
bracket_count
==
0
:
start_idx
=
i
bracket_count
+=
1
elif
char
==
']'
:
bracket_count
-=
1
if
bracket_count
==
0
and
start_idx
>=
0
:
try
:
json_str
=
result
[
start_idx
:
i
+
1
]
function_points
=
json
.
loads
(
json_str
)
logger
.
debug
(
"成功提取并解析JSON,功能点数量:
%
d"
,
len
(
function_points
))
return
function_points
except
:
break
logger
.
warning
(
"未找到有效的JSON数组格式"
)
logger
.
debug
(
"AI返回内容:
%
s"
,
result
[:
500
])
return
[]
def
analyze_coverage
(
self
,
function_points
:
List
[
Dict
],
test_cases
:
List
[
Dict
])
->
Dict
:
"""
分析测试用例覆盖情况
Args:
function_points: 功能点列表
test_cases: 测试用例列表
Returns:
覆盖率分析结果,包含:
- total_functions: 总功能数
- covered_functions: 已覆盖功能数
- coverage_rate: 覆盖率
- uncovered_functions: 未覆盖功能数
- coverage_details: 覆盖详情
"""
logger
.
info
(
"开始分析测试用例覆盖情况..."
)
# 构建已覆盖的功能集合
covered_descriptions
=
set
()
for
case
in
test_cases
:
desc
=
case
.
get
(
"功能描述"
,
""
)
if
desc
and
str
(
desc
)
.
strip
():
covered_descriptions
.
add
(
str
(
desc
)
.
strip
())
# 统计覆盖情况
total_functions
=
len
(
function_points
)
covered_count
=
0
uncovered_list
=
[]
for
fp
in
function_points
:
fp_desc
=
fp
.
get
(
"功能描述"
,
""
)
# 简单匹配:检查功能描述是否在测试用例中
is_covered
=
False
for
covered_desc
in
covered_descriptions
:
if
fp_desc
.
lower
()
in
covered_desc
.
lower
()
or
covered_desc
.
lower
()
in
fp_desc
.
lower
():
is_covered
=
True
break
if
is_covered
:
covered_count
+=
1
else
:
uncovered_list
.
append
(
fp
)
coverage_rate
=
covered_count
/
total_functions
if
total_functions
>
0
else
0
result
=
{
"total_functions"
:
total_functions
,
"covered_functions"
:
covered_count
,
"coverage_rate"
:
coverage_rate
,
"uncovered_functions"
:
total_functions
-
covered_count
,
"coverage_details"
:
{
"covered"
:
[
fp
for
fp
in
function_points
if
fp
not
in
uncovered_list
],
"uncovered"
:
uncovered_list
}
}
logger
.
info
(
"覆盖率分析完成:"
)
logger
.
info
(
" 总功能数:
%
d"
,
result
[
"total_functions"
])
logger
.
info
(
" 已覆盖:
%
d"
,
result
[
"covered_functions"
])
logger
.
info
(
" 未覆盖:
%
d"
,
result
[
"uncovered_functions"
])
logger
.
info
(
" 覆盖率:
%.1
f
%%
"
,
result
[
"coverage_rate"
]
*
100
)
return
result
def
analyze_by_module
(
self
,
test_cases
:
List
[
Dict
])
->
Dict
[
str
,
Dict
]:
"""
按功能模块分析测试用例
Args:
test_cases: 测试用例列表
Returns:
按模块分组的统计信息
"""
logger
.
info
(
"开始按功能模块分析..."
)
module_stats
=
{}
for
case
in
test_cases
:
module
=
case
.
get
(
"功能模块"
,
"未分类"
)
if
not
module
:
module
=
"未分类"
if
module
not
in
module_stats
:
module_stats
[
module
]
=
{
"count"
:
0
,
"cases"
:
[]
}
module_stats
[
module
][
"count"
]
+=
1
module_stats
[
module
][
"cases"
]
.
append
({
"name"
:
case
.
get
(
"用例名称"
,
""
),
"level"
:
case
.
get
(
"用例等级"
,
""
)
})
# 打印统计信息
logger
.
info
(
"功能模块统计:"
)
for
module
,
stats
in
sorted
(
module_stats
.
items
()):
logger
.
info
(
"
%
s:
%
d 条用例"
,
module
,
stats
[
"count"
])
return
module_stats
def
analyze_test_case_quality
(
test_cases
:
List
[
Dict
])
->
Dict
:
"""
分析测试用例质量
Args:
test_cases: 测试用例列表
Returns:
质量分析结果
"""
logger
.
info
(
"开始分析测试用例质量..."
)
quality_issues
=
[]
# 检查必填字段
required_fields
=
[
"用例名称"
,
"功能描述"
,
"STEP"
,
"预期结果"
]
for
i
,
case
in
enumerate
(
test_cases
,
1
):
for
field
in
required_fields
:
value
=
case
.
get
(
field
,
""
)
if
not
value
or
not
str
(
value
)
.
strip
():
quality_issues
.
append
({
"row"
:
case
.
get
(
"_row_number"
,
i
),
"issue"
:
f
"必填字段为空: {field}"
,
"case_name"
:
case
.
get
(
"用例名称"
,
""
)
})
# 检查用例编号格式
for
case
in
test_cases
:
case_number
=
case
.
get
(
"用例编号"
,
""
)
if
case_number
and
not
str
(
case_number
)
.
startswith
(
"TC-"
):
quality_issues
.
append
({
"row"
:
case
.
get
(
"_row_number"
,
""
),
"issue"
:
f
"用例编号格式不符合规范: {case_number}"
,
"case_name"
:
case
.
get
(
"用例名称"
,
""
)
})
result
=
{
"total_cases"
:
len
(
test_cases
),
"issues_count"
:
len
(
quality_issues
),
"issues"
:
quality_issues
,
"quality_rate"
:
(
len
(
test_cases
)
-
len
(
quality_issues
))
/
len
(
test_cases
)
if
test_cases
else
1.0
}
logger
.
info
(
"质量分析完成:"
)
logger
.
info
(
" 总用例数:
%
d"
,
result
[
"total_cases"
])
logger
.
info
(
" 问题数:
%
d"
,
result
[
"issues_count"
])
logger
.
info
(
" 质量率:
%.1
f
%%
"
,
result
[
"quality_rate"
]
*
100
)
return
result
AuxiliaryTool/DocumentAutoOptimizationCalibration/reports/新统一平台自动化部署操作指导_英文版.docx
浏览文件 @
ed7ea006
No preview for this file type
Docs/PRD/AI用例完善/问题处理/AI服务初始化失败_问题处理.md
0 → 100644
浏览文件 @
ed7ea006
# 问题描述
## 问题现象
-
运行强制使用API模式时,提示AI服务无法初始化,提示未检测到有效的Anthropic API密钥。
# 日志信息
```
ignorelang
2026-03-10 17:49:49 - src.main - INFO - 【步骤3】提取功能点
2026-03-10 17:49:49 - src.main - INFO - ------------------------------------------------------------
2026-03-10 17:49:49 - src.test_case_analyzer - INFO - 未检测到有效API密钥,尝试使用Claude CLI模式
2026-03-10 17:49:49 - src.claude_cli_adapter - INFO - Claude CLI适配器初始化完成
2026-03-10 17:49:49 - src.claude_cli_adapter - ERROR - 未找到claude命令,请确认已安装Claude Code CLI
2026-03-10 17:49:49 - src.claude_cli_adapter - ERROR - 安装方式: npm install -g @anthropic-ai/claude-code
2026-03-10 17:49:49 - src.main - ERROR - 处理失败: 无法初始化AI服务:
1. 未检测到有效的Anthropic API密钥
2. Claude CLI工具不可用
请至少配置其中一种方式
PS E:\GithubData\ubains-module-test\AuxiliaryTool\AIPerfectedTestCases>
```
Docs/PRD/AI用例完善/问题处理/AI服务初始化失败_问题处理_计划执行.md
0 → 100644
浏览文件 @
ed7ea006
# AI服务初始化失败_问题处理_计划执行
## 问题分析
### 问题现象
运行
`python run.py --mode api`
时,在步骤3提取功能点时失败,提示"未检测到有效的Anthropic API密钥"。
### 日志信息
```
2026-03-10 17:49:49 - src.test_case_analyzer - INFO - 未检测到有效API密钥,尝试使用Claude CLI模式
2026-03-10 17:49:49 - src.claude_cli_adapter - ERROR - 未找到claude命令
2026-03-10 17:49:49 - src.main - ERROR - 处理失败: 无法初始化AI服务:
1. 未检测到有效的Anthropic API密钥
2. Claude CLI工具不可用
```
### 根本原因
**main.py 已修复前缀验证**
,但
`TestCaseAnalyzer`
和
`AIServiceManager`
类中仍然存在前缀验证逻辑:
1.
**test_case_analyzer.py**
(第93行、第100行):
```
python
elif
api_key
and
api_key
.
startswith
(
API_KEY_PREFIX
):
# ❌
``
`
2.
**
ai_service_manager
.
py
**
(第
54
行、第
59
行、第
205
行):
``
`
python
if
api_key
and
api_key
.
startswith
(
API_KEY_PREFIX
):
# ❌
``
`
**
问题流程
**
:
1.
main
.
py
通过了
API
模式检查(传入
`use_cli=False`
和
`api_key`
)
2.
TestCaseAnalyzer
初始化时,检查
`api_key.startswith(API_KEY_PREFIX)`
3.
CCSwitch
格式的密钥不是
`sk-ant-api03-`
开头,检查失败
4.
尝试
CLI
模式,失败后抛出异常
## 修复方案
### 方案概述
移除所有类中的
`API_KEY_PREFIX`
前缀验证,改为仅检查密钥是否存在且非空。
### 详细实施步骤
#### 步骤1:修复 test_case_analyzer.py
-
[
]
第
93
行:移除
`api_key.startswith(API_KEY_PREFIX)`
检查
-
[
]
第
100
行:移除
`api_key_from_env.startswith(API_KEY_PREFIX)`
检查
#### 步骤2:修复 ai_service_manager.py
-
[
]
第
54
行:移除前缀检查
-
[
]
第
59
行:移除前缀检查
-
[
]
第
205
行:移除前缀检查
#### 步骤3:清理不再使用的导入
-
[
]
main
.
py
中移除
`API_KEY_PREFIX`
导入(可选,保留也无影响)
## 实施细节
### test_case_analyzer.py 修改内容
**
第
88
-
96
行修改前
**
:
```
python
if use_cli:
logger.info("强制使用Claude CLI模式")
self.ai_client = ClaudeCLIClient()
self.mode = "cli"
elif api_key and api_key.startswith(API_KEY_PREFIX):
logger.info("使用Anthropic API模式")
self.ai_client = AnthropicAPIClient(api_key)
self.mode = "api"
```
**修改后**:
```
python
if use_cli:
logger.info("强制使用Claude CLI模式")
self.ai_client = ClaudeCLIClient()
self.mode = "cli"
elif api_key and api_key.strip():
logger.info("使用Anthropic API模式")
self.ai_client = AnthropicAPIClient(api_key)
self.mode = "api"
```
**第97-115行修改前**:
```
python
else:
api_key_from_env = os.environ.get(API_KEY_ENV, "")
if api_key_from_env.startswith(API_KEY_PREFIX):
logger.info("检测到有效API密钥,使用Anthropic API模式")
self.ai_client = AnthropicAPIClient(api_key_from_env)
self.mode = "api"
else:
logger.info("未检测到有效API密钥,尝试使用Claude CLI模式")
try:
self.ai_client = ClaudeCLIClient()
self.mode = "cli"
except RuntimeError:
raise RuntimeError(...)
```
**修改后**:
```
python
else:
api_key_from_env = os.environ.get(API_KEY_ENV, "")
if api_key_from_env and api_key_from_env.strip():
logger.info("检测到API密钥,使用Anthropic API模式")
self.ai_client = AnthropicAPIClient(api_key_from_env)
self.mode = "api"
else:
logger.info("未检测到API密钥,尝试使用Claude CLI模式")
try:
self.ai_client = ClaudeCLIClient()
self.mode = "cli"
except RuntimeError:
raise RuntimeError(
"无法初始化AI服务:
\n
"
"1. 未设置Anthropic API密钥
\n
"
"2. Claude CLI工具不可用
\n
"
"请至少配置其中一种方式"
)
```
### ai_service_manager.py 修改内容
需要将所有
`api_key.startswith(API_KEY_PREFIX)`
改为
`api_key and api_key.strip()`
。
## 优化功能回填
### 已完成的优化
-
[
x
]
修复 test_case_analyzer.py 前缀验证(第93行、第100行)
-
[
x
]
修复 ai_service_manager.py 前缀验证(第54行、第59行、第205行)
-
[
x
]
更新错误提示信息(不再强调sk-ant-api03-格式)
### 待完成的优化
-
无
Docs/PRD/AI用例完善/问题处理/AI调用失败Extradata_问题处理.md
0 → 100644
浏览文件 @
ed7ea006
# 问题描述
## 问题现象
-
运行代码时提示,AI调用失败,Extra data: line 51 column 4 (char 1198)
# 日志信息
```
ignorelang
2026-03-10 17:59:22 - src.main - INFO - ------------------------------------------------------------
2026-03-10 17:59:22 - src.ai_service_manager - INFO - 使用自定义API endpoint: https://open.bigmodel.cn/api/anthropic
2026-03-10 17:59:22 - src.ai_service_manager - INFO - Anthropic API模式已启用
2026-03-10 17:59:22 - src.ai_service_manager - INFO - AI服务管理器初始化完成,模式: api
2026-03-10 17:59:22 - src.gap_detector - INFO - 缺失检测器初始化完成
2026-03-10 17:59:22 - src.gap_detector - INFO - 开始检测测试用例缺失...
2026-03-10 17:59:39 - httpx - INFO - HTTP Request: POST https://open.bigmodel.cn/api/anthropic/v1/messages "HTTP/1.1 200 OK"
2026-03-10 17:59:39 - src.gap_detector - ERROR - AI调用失败: Extra data: line 51 column 4 (char 1198)
2026-03-10 17:59:39 - src.gap_detector - INFO - 缺失检测完成:
2026-03-10 17:59:39 - src.gap_detector - INFO - 功能点缺失: 0 项
2026-03-10 17:59:39 - src.gap_detector - INFO - 场景覆盖不足: 0 项
2026-03-10 17:59:39 - src.gap_detector - INFO - 边界条件不足: 0 项
2026-03-10 17:59:39 - src.gap_detector - WARNING - ⚠️ 覆盖率未达标: 0.0% < 90.0%, 差距: 90.0%
2026-03-10 17:59:39 - src.gap_detector - INFO - 估算需新增测试用例: 0 条
2026-03-10 17:59:39 - src.main - INFO -
2026-03-10 17:59:39 - src.main - INFO - 【步骤6】生成新测试用例
2026-03-10 17:59:39 - src.main - INFO - ------------------------------------------------------------
```
Docs/PRD/AI用例完善/问题处理/AI调用失败Extradata_问题处理_计划执行.md
0 → 100644
浏览文件 @
ed7ea006
# AI调用失败Extra data_问题处理_计划执行
## 问题分析
### 问题现象
运行代码时AI调用失败,错误信息:"Extra data: line 51 column 4 (char 1198)"
### 日志信息
```
2026-03-10 17:59:39 - httpx - INFO - HTTP Request: POST https://open.bigmodel.cn/api/anthropic/v1/messages "HTTP/1.1 200 OK"
2026-03-10 17:59:39 - src.gap_detector - ERROR - AI调用失败: Extra data: line 51 column 4 (char 1198)
```
### 根本原因
1.
**JSON解析错误**
:
`json.JSONDecodeError`
的 "Extra data" 错误表示:
-
解析器成功解析了一个完整的JSON对象
-
但在该对象后面还有额外的数据
-
例如:
`{"a":1}{"b":2}`
或
`{"a":1} some text`
2.
**智谱AI(GLM)的返回格式可能与官方API不同**
:
-
可能包含额外的说明文字
-
可能返回多个JSON对象
-
可能使用不同的JSON格式
3.
**当前解析逻辑的限制**
:
```
python
# 当前代码直接使用json.loads()
return
json
.
loads
(
text
)
``
`
如果文本包含额外数据,会直接抛出异常
## 修复方案
### 方案概述
增强
JSON
解析逻辑,支持以下情况:
1.
提取第一个完整的
JSON
对象
/
数组
2.
忽略
JSON
前后的额外文本
3.
添加详细的调试日志以便排查问题
4.
使用正则表达式更准确地提取
JSON
### 详细实施步骤
#### 步骤1:增强 ai_service_manager.py 的JSON解析
-
[
]
修改
`_parse_json()`
方法,使用正则表达式提取
JSON
-
[
]
修改
`_parse_json_array()`
方法,增强解析能力
-
[
]
添加调试日志,记录原始响应内容
#### 步骤2:增强其他模块的JSON解析
-
[
]
test_case_analyzer
.
py
的
`_parse_function_points()`
-
[
]
gap_detector
.
py
的
`_parse_gap_result()`
-
[
]
case_generator
.
py
的
`_parse_case()`
-
[
]
统一使用增强的解析逻辑
#### 步骤3:添加通用JSON解析工具函数
-
[
]
创建统一的
JSON
解析工具函数
-
[
]
支持多种
JSON
格式
-
[
]
添加详细的错误处理
## 实施细节
### 新增通用JSON解析函数(在各模块中添加)
```
python
import re
import json
def extract_json(text: str) -> str:
"""
从文本中提取JSON字符串
支持以下格式:
1. 纯JSON:{"a":1}
2. 包含额外文本:text {"a":1} more text
3. 代码块格式:```json {"a":1} ```
Args:
text: 包含JSON的文本
Returns:
提取的JSON字符串
"""
# 尝试直接解析
try:
json.loads(text.strip())
return text.strip()
except:
pass
# 尝试从代码块中提取
code_block_pattern = r'```(?:json)?\s*\n?([\s\S]*?)\n?```'
matches = re.findall(code_block_pattern, text)
if matches:
for match in matches:
try:
json.loads(match.strip())
return match.strip()
except:
continue
# 尝试提取花括号内容(对象)
brace_pattern = r'\{[\s\S]*\}'
matches = re.findall(brace_pattern, text)
for match in matches:
try:
json.loads(match)
return match
except:
continue
# 尝试提取方括号内容(数组)
bracket_pattern = r'\[[\s\S]*\]'
matches = re.findall(bracket_pattern, text)
for match in matches:
try:
json.loads(match)
return match
except:
continue
return ""
def safe_parse_json(text: str, default=None):
"""
安全地解析JSON
Args:
text: 待解析的文本
default: 解析失败时的默认返回值
Returns:
解析后的Python对象,或默认值
"""
json_str = extract_json(text)
if not json_str:
logger.warning("未能提取到有效的JSON内容")
logger.debug("原始内容: %s", text[:500])
return default
try:
return json.loads(json_str)
except json.JSONDecodeError as e:
logger.error("JSON解析失败: %s", e)
logger.debug("提取的JSON字符串: %s", json_str[:500])
return default
```
### ai_service_manager.py 修改内容
```
python
def _parse_json(self, text: str) -> Dict:
"""解析JSON文本"""
import re
logger.debug("尝试解析JSON,内容长度: %d", len(text))
# 使用通用解析函数
result = safe_parse_json(text)
if result is not None:
return result
raise ValueError(f"无法解析JSON: {text[:200]}")
def _parse_json_array(self, text: str) -> List:
"""解析JSON数组文本"""
import re
logger.debug("尝试解析JSON数组,内容长度: %d", len(text))
# 使用通用解析函数
result = safe_parse_json(text, default=[])
if isinstance(result, list):
return result
# 如果结果是对象,尝试提取数组字段
if isinstance(result, dict):
for key, value in result.items():
if isinstance(value, list):
logger.info("从JSON对象中提取数组: %s", key)
return value
return []
```
### 其他模块同步修改
- test_case_analyzer.py
- gap_detector.py
- case_generator.py
各模块使用相同的 `extract_json()` 和 `safe_parse_json()` 函数。
## 优化功能回填
### 已完成的优化
- [x] 更新 ai_service_manager.py 的JSON解析逻辑(添加_extract_json方法)
- [x] 更新 test_case_analyzer.py 的JSON解析逻辑(增强_parse_function_points)
- [x] 更新 gap_detector.py 的JSON解析逻辑(增强_parse_gap_result)
- [x] 更新 case_generator.py 的JSON解析逻辑(增强_parse_case_result)
### 待完成的优化
- 无
### 修复说明
各模块的JSON解析已增强,支持:
1. 从代码块(```
json ...
```
)中提取JSON
2.
处理包含额外文本的情况
3.
使用括号匹配算法正确处理嵌套的JSON对象/数组
4.
添加详细的调试日志
Docs/PRD/AI用例完善/问题处理/API模式执行提示调用失败_问题处理.md
0 → 100644
浏览文件 @
ed7ea006
# 问题描述
## 问题现象
-
运行强制使用API模式时,提示API调用失败,提示错误码为403 Forbidden,错误信息为Request not allowed,导致缺失检测无法完成。
# 日志信息
```
ignorelang
2026-03-10 17:52:54 - src.main - INFO - ------------------------------------------------------------
2026-03-10 17:52:54 - src.ai_service_manager - INFO - Anthropic API模式已启用
2026-03-10 17:52:54 - src.ai_service_manager - INFO - AI服务管理器初始化完成,模式: api
2026-03-10 17:52:54 - src.gap_detector - INFO - 缺失检测器初始化完成
2026-03-10 17:52:54 - src.gap_detector - INFO - 开始检测测试用例缺失...
2026-03-10 17:52:55 - httpx - INFO - HTTP Request: POST https://api.anthropic.com/v1/messages "HTTP/1.1 403 Forbidden"
2026-03-10 17:52:55 - src.gap_detector - ERROR - AI调用失败: Error code: 403 - {'error': {'type': 'forbidden', 'message': 'Request not allowed'}}
2026-03-10 17:52:55 - src.gap_detector - INFO - 缺失检测完成:
2026-03-10 17:52:55 - src.gap_detector - INFO - 功能点缺失: 0 项
2026-03-10 17:52:55 - src.gap_detector - INFO - 场景覆盖不足: 0 项
2026-03-10 17:52:55 - src.gap_detector - INFO - 边界条件不足: 0 项
2026-03-10 17:52:55 - src.gap_detector - WARNING - ⚠️ 覆盖率未达标: 0.0% < 90.0%, 差距: 90.0%
2026-03-10 17:52:55 - src.gap_detector - INFO - 估算需新增测试用例: 0 条
2026-03-10 17:52:55 - src.main - INFO -
2026-03-10 17:52:55 - src.main - INFO - 【步骤6】生成新测试用例
2026-03-10 17:52:55 - src.main - INFO - ------------------------------------------------------------
```
Docs/PRD/AI用例完善/问题处理/API模式执行提示调用失败_问题处理_计划执行.md
0 → 100644
浏览文件 @
ed7ea006
# API模式执行提示调用失败_问题处理_计划执行
## 问题分析
### 问题现象
运行
`python run.py --mode api`
时,API调用返回 403 Forbidden 错误,错误信息为 "Request not allowed"。
### 日志信息
```
2026-03-10 17:52:55 - httpx - INFO - HTTP Request: POST https://api.anthropic.com/v1/messages "HTTP/1.1 403 Forbidden"
2026-03-10 17:52:55 - src.gap_detector - ERROR - AI调用失败: Error code: 403 - {'error': {'type': 'forbidden', 'message': 'Request not allowed'}}
```
### 根本原因
1.
**用户的API密钥格式**
:
`845694a2ffd64b6c93cc7359d760234d.j3FuzTgfmreM9qNU`
-
这是CCSwitch等代理服务的密钥格式
-
不是官方Anthropic API的密钥格式
2.
**代码调用的是官方endpoint**
:
-
当前代码请求:
`https://api.anthropic.com/v1/messages`
-
CCSwitch等代理需要使用其自定义endpoint
3.
**Anthropic客户端初始化缺少base_url参数**
:
```
python
# 当前代码
self
.
client
=
anthropic
.
Anthropic
(
api_key
=
api_key
)
# 需要改为
self
.
client
=
anthropic
.
Anthropic
(
api_key
=
api_key
,
base_url
=
custom_endpoint
# 支持自定义endpoint
)
``
`
## 修复方案
### 方案概述
添加自定义
API
endpoint
支持,允许用户通过环境变量配置代理服务的
API
地址。
### 详细实施步骤
#### 步骤1:修改 config.py
-
[
]
添加
`API_BASE_URL`
配置常量
-
[
]
添加
`get_api_base_url()`
函数,从环境变量读取自定义
endpoint
-
[
]
更新
`get_project_info()`
返回
endpoint
信息
#### 步骤2:修改 test_case_analyzer.py
-
[
]
修改
`AnthropicAPIClient`
初始化,支持自定义
base_url
-
[
]
添加
endpoint
日志输出
#### 步骤3:修改 ai_service_manager.py
-
[
]
修改
`_init_api_mode`
方法,支持自定义
base_url
#### 步骤4:更新 .env.example
-
[
]
添加
`API_BASE_URL`
配置说明
## 实施细节
### config.py 修改内容
```
python
# ==================== AI配置 ====================
# API基础URL(可选,用于代理服务)
# 官方API: https://api.anthropic.com
# CCSwitch等代理: 根据服务商提供的地址配置
API_BASE_URL = "https://api.anthropic.com"
def get_api_base_url() -> str:
"""
获取API基础URL
优先级:环境变量 > 配置文件常量
Returns:
API基础URL
"""
import os
return os.environ.get("API_BASE_URL", API_BASE_URL)
```
### test_case_analyzer.py 修改内容
```
python
class AnthropicAPIClient(AIClientInterface):
"""Anthropic API客户端"""
def __init__(self, api_key: str, base_url: Optional[str] = None):
if anthropic is None:
raise ImportError("未安装anthropic库,请运行: pip install anthropic")
kwargs = {"api_key": api_key}
if base_url:
kwargs["base_url"] = base_url
logger.info("使用自定义API endpoint: %s", base_url)
self.client = anthropic.Anthropic(**kwargs)
```
同时修改TestCaseAnalyzer的初始化:
```
python
from src.config import get_api_base_url
class TestCaseAnalyzer:
def __init__(self, api_key: Optional
[
str
]
= None, use_cli: bool = False):
# ... 原有逻辑 ...
elif api_key and api_key.strip():
base_url = get_api_base_url()
logger.info("使用Anthropic API模式")
self.ai_client = AnthropicAPIClient(api_key, base_url)
# ...
```
### ai_service_manager.py 修改内容
```
python
def _init_api_mode(self, api_key: str):
"""初始化API模式"""
from src.config import get_api_base_url
try:
import anthropic
base_url = get_api_base_url()
kwargs = {"api_key": api_key}
if base_url != "https://api.anthropic.com":
kwargs["base_url"] = base_url
logger.info("使用自定义API endpoint: %s", base_url)
self.api_client = anthropic.Anthropic(**kwargs)
logger.info("Anthropic API模式已启用")
except ImportError:
raise RuntimeError("未安装anthropic库,请运行: pip install anthropic")
```
### .env.example 更新内容
```
bash
# ==================== API配置(可选)====================
# API基础URL(用于代理服务)
# 官方API可留空或设置为:https://api.anthropic.com
# CCSwitch等代理服务需设置对应endpoint
API_BASE_URL=https://api.anthropic.com
```
## 使用说明
### 官方API用户
无需额外配置,使用默认endpoint即可。
### CCSwitch等代理用户
在 `.env` 文件中配置:
```
bash
ANTHROPIC_API_KEY=your-key-here
API_BASE_URL=https://your-proxy-endpoint.com
```
## 优化功能回填
### 已完成的优化
-
[
x
]
添加API_BASE_URL配置支持(config.py)
-
[
x
]
添加get_api_base_url()函数(从环境变量读取)
-
[
x
]
修改AnthropicAPIClient支持自定义endpoint(test_case_analyzer.py)
-
[
x
]
修改TestCaseAnalyzer传入base_url参数
-
[
x
]
修改AIServiceManager._init_api_mode支持自定义endpoint
-
[
x
]
更新.env.example配置说明
### 待完成的优化
-
无
Docs/PRD/AI用例完善/问题处理/代码运行报错unexpectedindent_问题处理.md
0 → 100644
浏览文件 @
ed7ea006
# 问题描述
## 问题现象
-
运行代码时报错:unexpected indent (test_case_analyzer.py, line 348)
# 日志信息
```
ignorelang
错误: unexpected indent (test_case_analyzer.py, line 348)
Traceback (most recent call last):
File "E:\GithubData\ubains-module-test\AuxiliaryTool\AIPerfectedTestCases\run.py", line 332, in <module>
exit_code = main()
File "E:\GithubData\ubains-module-test\AuxiliaryTool\AIPerfectedTestCases\run.py", line 327, in main
return run_cli(args)
File "E:\GithubData\ubains-module-test\AuxiliaryTool\AIPerfectedTestCases\run.py", line 311, in run_cli
from src.main import main as run_main
File "E:\GithubData\ubains-module-test\AuxiliaryTool\AIPerfectedTestCases\src\main.py", line 41, in <module>
from src.test_case_analyzer import TestCaseAnalyzer, analyze_test_case_quality
File "E:\GithubData\ubains-module-test\AuxiliaryTool\AIPerfectedTestCases\src\test_case_analyzer.py", line 348
return []
IndentationError: unexpected indent
```
Docs/PRD/AI用例完善/问题处理/代码运行报错unexpectedindent_问题处理_计划执行.md
0 → 100644
浏览文件 @
ed7ea006
# 代码运行报错unexpected indent_问题处理_计划执行
## 问题分析
### 问题现象
运行代码时报错:
`unexpected indent (test_case_analyzer.py, line 348)`
### 日志信息
```
IndentationError: unexpected indent
File "E:\...\test_case_analyzer.py", line 348
return []
```
### 根本原因
**Python缩进错误**
:在修改
`test_case_analyzer.py`
的
`_parse_function_points()`
方法时,第348行的
`return []`
语句被错误地缩进了。
**问题代码**
(第346-348行):
```
python
logger
.
warning
(
"未找到有效的JSON数组格式"
)
logger
.
debug
(
"AI返回内容:
%
s"
,
result
[:
500
])
return
[]
# ❌ 缩进过多
```
**正确代码**
应该是:
```
python
logger
.
warning
(
"未找到有效的JSON数组格式"
)
logger
.
debug
(
"AI返回内容:
%
s"
,
result
[:
500
])
return
[]
# ✅ 与函数内其他代码同级缩进
```
## 修复方案
### 方案概述
修正
`test_case_analyzer.py`
第348行的缩进,使其与函数内其他代码保持同级。
### 实施细节
将第348行从:
```
python
return
[]
# 8个空格缩进(错误)
```
改为:
```
python
return
[]
# 4个空格缩进(正确,与函数内代码同级)
```
实际上,正确的缩进应该是4个空格(一级缩进),因为这是在
`TestCaseAnalyzer`
类的方法内部。
## 优化功能回填
### 已完成的优化
-
[
x
]
修正 test_case_analyzer.py 第348行的缩进错误
### 待完成的优化
-
无
Docs/PRD/AI用例完善/问题处理/代码运行提示API权限不足_问题处理.md
0 → 100644
浏览文件 @
ed7ea006
# 问题描述
## 问题现象
-
在执行代码后提示API权限不足,我提供的API密钥是正确的,因为我可以正常使用claude code CLI工具。
# 日志信息
```
ignorelang
PS E:\GithubData\ubains-module-test\AuxiliaryTool\AIPerfectedTestCases> python run.py --test-api
【测试API连接】
API密钥: 845694a2...
AI模型: claude-sonnet-4-6
发送测试请求...
✗ API权限不足
请检查API密钥是否有调用权限
PS E:\GithubData\ubains-module-test\AuxiliaryTool\AIPerfectedTestCases>
```
\ No newline at end of file
Docs/PRD/AI用例完善/问题处理/代码运行提示API权限不足_问题处理_计划执行.md
0 → 100644
浏览文件 @
ed7ea006
# 代码运行提示API权限不足_问题处理_计划执行
## 1. 问题描述
### 问题现象
执行
`python run.py --test-api`
后提示API权限不足,但用户确认API密钥是正确的,可以正常使用claude code CLI工具。
### 错误信息
```
✗ API权限不足
请检查API密钥是否有调用权限
```
### 当前API密钥格式
```
845694a2ffd64b6c93cc7359d760234d.j3FuzTgfmreM9qNU
```
---
## 2. 问题分析
### 2.1 根本原因
**问题核心**
:用户混淆了两种不同的密钥:
| 密钥类型 | 格式 | 用途 | 获取方式 |
|---------|------|------|----------|
|
**Anthropic API密钥**
|
`sk-ant-api03-xxxxx`
| 通过Python代码调用Claude API | https://console.anthropic.com/ |
|
**Claude Code Session Token**
|
`xxxxx.xxxxx`
(用户提供的格式) | 使用claude code CLI工具 | Claude Code CLI自动生成 |
**当前情况**
:
-
用户提供的是
**Claude Code Session Token**
-
代码需要的是
**Anthropic API密钥**
-
两种密钥
**不互通**
### 2.2 为什么会出现这个问题?
1.
**Claude Code CLI**
可以使用Session Token直接与Claude交互
2.
**Python anthropic包**
需要使用官方API密钥
3.
两者是
**不同的认证方式**
### 2.3 验证方法
检查密钥格式:
-
✅ 正确的API密钥:
`sk-ant-api03-`
开头
-
❌ 用户提供的:
`845694a2ffd64b6c93cc7359d760234d.`
格式(Session Token)
---
## 3. 解决方案
### 方案A:获取正确的Anthropic API密钥(推荐)
#### 步骤1:访问Anthropic控制台
1.
打开浏览器,访问:https://console.anthropic.com/
2.
登录你的Anthropic账户
#### 步骤2:创建API密钥
1.
点击左侧菜单
**"API Keys"**
2.
点击
**"Create API Key"**
按钮
3.
输入密钥名称(如:AI测试用例工具)
4.
点击
**"Create Key"**
5.
**复制生成的密钥**
(格式:
`sk-ant-api03-xxxxx`
)
#### 步骤3:更新配置
1.
打开
`.env`
文件
2.
将
`ANTHROPIC_API_KEY`
的值替换为新创建的API密钥
3.
保存文件
#### 步骤4:验证
```
bash
python run.py
--test-api
```
预期输出:
```
✓ API连接正常
AI回复: API连接成功
```
---
### 方案B:使用Claude Code CLI作为后端(备选方案)
如果用户无法获取API密钥,可以考虑直接调用claude code CLI工具。
#### 工作原理
```
Python代码 → subprocess调用 → claude CLI进程 → 发送请求 → 返回结果 → 进程退出
↑ ↓
└──────────────── 自动拉起,用完即退出 ──────────────────────┘
```
**无需保持CLI工具运行**
:每次需要AI功能时,代码会自动启动新的claude进程,处理完成后自动退出。
#### 前置条件
1.
✅ 系统已安装 Claude Code CLI 工具
2.
✅
`claude`
命令在系统 PATH 中可用
3.
✅ 已登录过一次(Session Token已保存)
#### 实现方式
**新增模块:`src/claude_cli_adapter.py`**
```
python
# -*- coding: utf-8 -*-
"""
Claude CLI适配器模块
通过subprocess调用claude命令行工具
"""
import
subprocess
import
json
from
typing
import
Optional
,
Dict
,
Any
from
logging
import
getLogger
logger
=
getLogger
(
__name__
)
class
ClaudeCLIAdapter
:
"""Claude CLI适配器"""
def
__init__
(
self
):
"""初始化适配器"""
self
.
timeout
=
300
# 5分钟超时
def
check_available
(
self
)
->
bool
:
"""检查claude命令是否可用"""
try
:
result
=
subprocess
.
run
(
[
"claude"
,
"--version"
],
capture_output
=
True
,
text
=
True
,
timeout
=
10
)
if
result
.
returncode
==
0
:
logger
.
info
(
"Claude CLI版本:
%
s"
,
result
.
stdout
.
strip
())
return
True
return
False
except
FileNotFoundError
:
logger
.
error
(
"未找到claude命令,请确认已安装Claude Code CLI"
)
return
False
except
Exception
as
e
:
logger
.
error
(
"检查claude命令失败:
%
s"
,
e
)
return
False
def
ask
(
self
,
prompt
:
str
,
model
:
str
=
"claude-sonnet-4-6"
)
->
str
:
"""
调用claude ask命令
Args:
prompt: 提示词
model: 模型名称
Returns:
AI回复文本
"""
logger
.
info
(
"调用Claude CLI..."
)
logger
.
debug
(
"提示词长度:
%
d 字符"
,
len
(
prompt
))
try
:
result
=
subprocess
.
run
(
[
"claude"
,
"ask"
,
"--model"
,
model
,
prompt
],
capture_output
=
True
,
text
=
True
,
timeout
=
self
.
timeout
,
encoding
=
"utf-8"
)
if
result
.
returncode
==
0
:
response
=
result
.
stdout
.
strip
()
logger
.
info
(
"Claude CLI调用成功,返回
%
d 字符"
,
len
(
response
))
return
response
else
:
logger
.
error
(
"Claude CLI调用失败:
%
s"
,
result
.
stderr
)
raise
RuntimeError
(
f
"Claude CLI error: {result.stderr}"
)
except
subprocess
.
TimeoutExpired
:
logger
.
error
(
"Claude CLI调用超时"
)
raise
TimeoutError
(
"Claude CLI调用超时"
)
except
Exception
as
e
:
logger
.
error
(
"Claude CLI调用异常:
%
s"
,
e
)
raise
def
ask_json
(
self
,
prompt
:
str
,
model
:
str
=
"claude-sonnet-4-6"
)
->
Dict
:
"""
调用claude ask命令,并解析JSON返回
Args:
prompt: 提示词
model: 模型名称
Returns:
解析后的JSON对象
"""
# 在提示词中要求返回JSON
if
"JSON"
not
in
prompt
and
"json"
not
in
prompt
:
prompt
=
prompt
+
"
\n\n
请输出JSON格式的结果。"
response
=
self
.
ask
(
prompt
,
model
)
return
self
.
_parse_json_response
(
response
)
def
_parse_json_response
(
self
,
response
:
str
)
->
Dict
:
"""解析JSON响应"""
try
:
return
json
.
loads
(
response
)
except
json
.
JSONDecodeError
:
# 尝试提取JSON部分
start
=
response
.
find
(
"{"
)
end
=
response
.
rfind
(
"}"
)
+
1
if
start
>=
0
and
end
>
start
:
json_str
=
response
[
start
:
end
]
return
json
.
loads
(
json_str
)
raise
ValueError
(
"无法解析JSON响应"
)
```
**修改主模块以支持两种方式**
**文件:`src/main.py`**
```
python
# 检测使用哪种方式
def
get_ai_client
():
"""获取AI客户端"""
api_key
=
os
.
environ
.
get
(
API_KEY_ENV
)
if
api_key
and
api_key
.
startswith
(
"sk-ant-api03-"
):
# 使用Anthropic API
logger
.
info
(
"使用Anthropic API模式"
)
return
{
"type"
:
"api"
,
"client"
:
TestCaseAnalyzer
(
api_key
),
"detector"
:
GapDetector
(
api_key
),
"generator"
:
CaseGenerator
(
api_key
)
}
else
:
# 使用Claude CLI
logger
.
info
(
"使用Claude CLI模式"
)
from
src.claude_cli_adapter
import
ClaudeCLIAdapter
adapter
=
ClaudeCLIAdapter
()
if
not
adapter
.
check_available
():
raise
RuntimeError
(
"Claude CLI不可用,且未配置有效的API密钥"
)
return
{
"type"
:
"cli"
,
"adapter"
:
adapter
}
```
#### 优缺点对比
| 优点 | 缺点 |
|------|------|
| ✅ 无需API密钥 | ⚠️ 需要安装Claude Code CLI |
| ✅ 利用现有Session Token | ⚠️ 调用速度较API慢 |
| ✅ 无额外API费用 | ⚠️ 依赖外部工具 |
| ✅ 配置简单 | ⚠️ 错误处理较复杂 |
#### 使用说明
```
bash
# 1. 确认claude命令可用
claude
--version
# 2. 正常运行(自动检测模式)
python run.py
# 3. 强制使用CLI模式(可选)
python run.py
--use-cli
```
text=True
)
return result.stdout
```
#### 优缺点
| 优点 | 缺点 |
|------|------|
| 无需API密钥 | 需要安装claude code CLI |
| 利用现有Session Token | 调用效率较低 |
| 无额外费用 | 依赖外部工具 |
**注意**:此方案需要用户已安装claude code CLI工具。
---
### 方案C:使用其他免费AI API(替代方案)
如果无法获取Anthropic API密钥,可以考虑使用其他支持免费额度的AI服务:
| 服务 | 免费额度 | 说明 |
|------|----------|------|
| Groq | 免费额度较高 | 支持Llama等模型 |
| Hugging Face | 有限免费 | 支持多种开源模型 |
| DeepSeek | 有限免费 | 支持DeepSeek模型 |
---
## 4. 推荐方案
**强烈推荐使用方案A**:获取正确的Anthropic API密钥
### 理由
1. **稳定性**:官方API最稳定可靠
2. **性能**:直接API调用性能最好
3. **功能完整**:支持所有AI功能
4. **成本可控**:可以监控和控制使用量
### 费用说明
- **Claude Sonnet 4.6**:$3 / 1M input tokens
- **Claude Opus 4.6**:$15 / 1M input tokens
- 对于测试用例生成,通常成本很低(几美分级别)
---
## 5. 实施计划
### 5.1 短期方案(立即执行)
| 步骤 | 操作 | 负责人 |
|------|------|--------|
| 1 | 访问Anthropic控制台获取API密钥 | 用户 |
| 2 | 更新.env文件 | 用户 |
| 3 | 运行测试验证 | 用户 |
### 5.2 中期方案(可选)
| 步骤 | 操作 | 说明 |
|------|------|------|
| 1 | 添加多AI服务支持 | 支持Groq、DeepSeek等 |
| 2 | 添加成本预估 | 显示预估费用 |
| 3 | 添加用量监控 | 追踪API调用次数 |
### 5.3 长期方案(考虑中)
| 步骤 | 操作 | 说明 |
|------|------|------|
| 1 | 支持Claude Code CLI调用 | 作为备选方案 |
| 2 | 本地模型支持 | 使用Ollama等运行本地模型 |
---
## 6. API密钥获取指南
### 详细步骤
1. **打开控制台**
```
https://console.anthropic.com/
```
2. **登录账户**
- 使用邮箱登录
- 或使用Google/GitHub登录
3. **创建API密钥**
- 左侧菜单选择 "API Keys"
- 点击 "Create API Key" 按钮
- 输入名称(可选)
- 选择权限(通常默认即可)
- 点击 "Create Key"
4. **复制密钥**
- 密钥格式:`sk-ant-api03-xxxxx`
- ⚠️ **只在创建时显示一次,请妥善保存**
5. **更新配置文件**
```bash
# 编辑 .env 文件
ANTHROPIC_API_KEY=sk-ant-api03-xxxxx
```
### 安全建议
- ✅ 不要将API密钥提交到代码仓库
- ✅ 不要在公开场合分享API密钥
- ✅ 定期轮换API密钥
- ✅ 为不同项目使用不同的API密钥
---
## 7. 常见问题
### Q1: 我没有信用卡怎么办?
**A**: Anthropic需要绑定信用卡才能创建API密钥。如果没有信用卡,可以考虑:
- 使用预付卡/虚拟卡
- 使用方案B(Claude Code CLI)
- 使用方案C(其他免费AI服务)
### Q2: API密钥的费用如何计算?
**A**: 按token使用量计费:
- 输入tokens:发送给AI的文字
- 输出tokens:AI返回的文字
- 大约1美元可以处理数十万个汉字
### Q3: 如何监控API使用量?
**A**: 在Anthropic控制台的 "Usage" 页面可以查看详细的使用记录和费用。
### Q4: 为什么我的Session Token不能用于API调用?
**A**: 两种密钥使用不同的认证协议:
- API密钥:基于HTTP Header认证
- Session Token:基于WebSocket认证(Claude Code专用)
---
## 8. 实施状态
| 项目 | 状态 |
|------|------|
| 问题分析 | ✅ 完成 |
| 解决方案设计 | ✅ 完成 |
| 用户等待获取API密钥 | ⏳ 进行中 |
---
## 9. 后续支持
如果获取API密钥后仍有问题,请提供以下信息:
1. API测试结果
```
bash
python run.py --test-api
```
2. 详细日志
```
bash
python run.py --test-api --log-level DEBUG
```
3. API密钥格式确认
- 确认密钥以 `sk-ant-api03-` 开头
- 确认密钥完整复制(没有多余空格)
---
## 10. 附录:密钥格式对比
### 正确的API密钥格式
```
sk-ant-api03-038umqPGqEu3K8XjrV8XA7mBKGc1UHrEjBKL3fF
```
- 长度:约60个字符
- 前缀:`sk-ant-api03-`
### Claude Code Session Token格式
```
845694a2ffd64b6c93cc7359d760234d.j3FuzTgfmreM9qNU
```
-
长度:约50个字符
-
包含点号分隔符
**这两种密钥不互通!**
Docs/PRD/AI用例完善/问题处理/代码运行提示ClaudeCLT不可用_问题处理.md
0 → 100644
浏览文件 @
ed7ea006
# 问题描述
## 问题现象
-
在执行代码后提示ClaudeCLT不可哦那个,请确认已安装Claude Code CLI。
# 日志信息
```
ignorelang
2026-03-10 17:39:00 - src.main - INFO - 【步骤3】提取功能点
2026-03-10 17:39:00 - src.main - INFO - ------------------------------------------------------------
2026-03-10 17:39:00 - src.test_case_analyzer - INFO - 强制使用Claude CLI模式
2026-03-10 17:39:00 - src.claude_cli_adapter - INFO - Claude CLI适配器初始化完成
2026-03-10 17:39:00 - src.claude_cli_adapter - ERROR - 未找到claude命令,请确认已安装Claude Code CLI
2026-03-10 17:39:00 - src.claude_cli_adapter - ERROR - 安装方式: npm install -g @anthropic-ai/claude-code
2026-03-10 17:39:00 - src.main - ERROR - 处理失败: Claude CLI不可用
```
\ No newline at end of file
Docs/PRD/AI用例完善/问题处理/代码运行提示ClaudeCLT不可用_问题处理_计划执行.md
0 → 100644
浏览文件 @
ed7ea006
# 代码运行提示ClaudeCLI不可用_问题处理_计划执行
## 问题分析
### 问题现象
在执行AI完善测试用例工具后,提示"Claude CLI不可用",程序无法继续运行。
### 日志信息
```
2026-03-10 17:39:00 - src.main - INFO - 【步骤3】提取功能点
2026-03-10 17:39:00 - src.test_case_analyzer - INFO - 强制使用Claude CLI模式
2026-03-10 17:39:00 - src.claude_cli_adapter - INFO - Claude CLI适配器初始化完成
2026-03-10 17:39:00 - src.claude_cli_adapter - ERROR - 未找到claude命令,请确认已安装Claude Code CLI
2026-03-10 17:39:00 - src.claude_cli_adapter - ERROR - 安装方式: npm install -g @anthropic-ai/claude-code
2026-03-10 17:39:00 - src.main - ERROR - 处理失败: Claude CLI不可用
```
### 根本原因
1.
**工具支持两种AI服务模式**
:
-
API模式:使用Anthropic官方API(需要API密钥)
-
CLI模式:使用本地Claude CLI工具(需要安装
`@anthropic-ai/claude-code`
)
2.
**当前问题**
:
-
`src/main.py`
第190行:
`analyzer = TestCaseAnalyzer(api_key=api_key, use_cli=use_cli)`
-
当
`use_cli=True`
时,强制使用CLI模式
-
如果用户未安装Claude CLI,程序会报错退出
3.
**配置未被正确使用**
:
-
`src/config.py`
中定义了
`AI_SERVICE_MODE = "auto"`
-
但main.py未使用此配置,而是通过参数传递
`use_cli`
## 修复方案
### 方案概述
1.
修改
`src/main.py`
:使用
`AI_SERVICE_MODE`
配置决定AI服务模式
2.
修改
`src/config.py`
:添加从环境变量读取配置的功能
3.
修改
`run.py`
:添加命令行参数支持
4.
添加
`.env.example`
文件:提供配置模板
### 详细实施步骤
#### 步骤1:修改config.py
-
[
]
添加从环境变量读取
`AI_SERVICE_MODE`
的功能
-
[
]
添加获取AI服务模式的函数
#### 步骤2:修改main.py
-
[
]
使用
`get_ai_service_mode()`
函数获取服务模式
-
[
]
根据
`AI_SERVICE_MODE`
配置决定使用API还是CLI模式
-
[
]
改进错误提示,引导用户正确配置
#### 步骤3:修改run.py
-
[
]
添加
`--mode`
命令行参数,支持api/cli/auto选项
-
[
]
添加
`--api-key`
命令行参数,直接指定API密钥
#### 步骤4:更新.env.example
-
[
]
添加
`AI_SERVICE_MODE`
配置项说明
## 实施细节
### config.py 修改内容
```
python
# 添加函数
def
get_ai_service_mode
()
->
str
:
"""
获取AI服务模式
优先级:环境变量 > 配置文件常量
Returns:
服务模式:'auto', 'api', 'cli'
"""
import
os
return
os
.
environ
.
get
(
"AI_SERVICE_MODE"
,
AI_SERVICE_MODE
)
```
### main.py 修改内容
```
python
# 修改main函数,使用配置
def
main
(
use_cli
:
bool
=
False
)
->
int
:
# 检查AI服务模式
api_key
=
None
service_mode
=
get_ai_service_mode
()
# 命令行参数优先
if
use_cli
:
service_mode
=
"cli"
elif
os
.
environ
.
get
(
API_KEY_ENV
):
api_key
=
os
.
environ
.
get
(
API_KEY_ENV
)
if
api_key
.
startswith
(
API_KEY_PREFIX
):
service_mode
=
"api"
# 根据模式初始化
if
service_mode
==
"api"
:
if
not
api_key
or
not
api_key
.
startswith
(
API_KEY_PREFIX
):
logger
.
error
(
"API模式需要有效的ANTHROPIC_API_KEY环境变量"
)
logger
.
error
(
"请设置: set ANTHROPIC_API_KEY=sk-ant-api03-..."
)
return
1
use_cli
=
False
elif
service_mode
==
"cli"
:
use_cli
=
True
# auto模式保持原有逻辑
```
### run.py 修改内容
```
python
import
argparse
def
main
():
parser
=
argparse
.
ArgumentParser
(
description
=
"AI完善测试用例工具"
)
parser
.
add_argument
(
"--mode"
,
choices
=
[
"auto"
,
"api"
,
"cli"
],
default
=
"auto"
,
help
=
"AI服务模式"
)
parser
.
add_argument
(
"--api-key"
,
help
=
"Anthropic API密钥"
)
args
=
parser
.
parse_args
()
# 设置环境变量
if
args
.
mode
:
os
.
environ
[
"AI_SERVICE_MODE"
]
=
args
.
mode
if
args
.
api_key
:
os
.
environ
[
API_KEY_ENV
]
=
args
.
api_key
# 执行主程序
from
src.main
import
main
sys
.
exit
(
main
())
```
### .env.example 更新内容
```
bash
# Anthropic API密钥(可选)
# 获取方式:https://console.anthropic.com/
# ANTHROPIC_API_KEY=sk-ant-api03-...
# AI服务模式(可选)
# auto: 自动检测(默认)
# api: 强制使用Anthropic API
# cli: 强制使用Claude CLI
AI_SERVICE_MODE
=
api
```
## 优化功能回填
### 已完成的优化
-
[
x
]
添加AI服务模式配置支持(config.py新增get_ai_service_mode函数)
-
[
x
]
添加命令行参数支持(run.py新增--mode和--api-key参数)
-
[
x
]
改进错误提示信息(main.py新增详细的配置引导)
-
[
x
]
更新.env.example配置模板(添加AI_SERVICE_MODE说明)
### 待完成的优化
-
无
Docs/PRD/AI用例完善/问题处理/强制代码使用API模式失败_问题处理.md
0 → 100644
浏览文件 @
ed7ea006
# 问题描述
## 问题现象
-
运行强制使用API模式时,提示API模式需要有效的ANTHROPIC_API_KEY环境变量,导致无法启动。
# 日志信息
```
ignorelang
2026-03-10 17:47:13 - src.main - INFO - 日志系统初始化完成,级别=INFO
2026-03-10 17:47:13 - src.main - INFO - ============================================================
2026-03-10 17:47:13 - src.main - INFO - AI完善测试用例工具 启动
2026-03-10 17:47:13 - src.main - INFO - 版本: 1.0.0
2026-03-10 17:47:13 - src.main - INFO - ============================================================
2026-03-10 17:47:13 - src.main - ERROR - API模式需要有效的ANTHROPIC_API_KEY环境变量
2026-03-10 17:47:13 - src.main - ERROR - 请设置API密钥:
2026-03-10 17:47:13 - src.main - ERROR - Windows: set ANTHROPIC_API_KEY=sk-ant-api03-...
2026-03-10 17:47:13 - src.main - ERROR - Linux/Mac: export ANTHROPIC_API_KEY=sk-ant-api03-...
2026-03-10 17:47:13 - src.main - ERROR - 或在 .env 文件中配置: ANTHROPIC_API_KEY=sk-ant-api03-...
2026-03-10 17:47:13 - src.main - ERROR -
2026-03-10 17:47:13 - src.main - ERROR - 如需使用Claude CLI模式,请设置:
2026-03-10 17:47:13 - src.main - ERROR - 环境变量: set AI_SERVICE_MODE=cli
2026-03-10 17:47:13 - src.main - ERROR - 或命令行参数: python run.py --mode cli
```
Docs/PRD/AI用例完善/问题处理/强制代码使用API模式失败_问题处理_计划执行.md
0 → 100644
浏览文件 @
ed7ea006
# 强制代码使用API模式失败_计划执行
## 问题分析
### 问题现象
运行
`python run.py --mode api`
时,提示"API模式需要有效的ANTHROPIC_API_KEY环境变量",程序无法启动。
### 日志信息
```
2026-03-10 17:47:13 - src.main - ERROR - API模式需要有效的ANTHROPIC_API_KEY环境变量
2026-03-10 17:47:13 - src.main - ERROR - 请设置API密钥:
2026-03-10 17:47:13 - src.main - ERROR - Windows: set ANTHROPIC_API_KEY=sk-ant-api03-...
```
### 根本原因
1.
**代码验证逻辑过于严格**
:
-
`src/main.py`
中的 API 模式验证逻辑检查密钥是否以
`sk-ant-api03-`
开头
-
用户的密钥格式是 CCSwitch 格式:
`845694a2ffd64b6c93cc7359d760234d.j3FuzTgfmreM9qNU`
-
密钥本身是有效的,但不满足前缀验证,被判定为无效
2.
**验证代码位置**
:
```
python
if
service_mode
==
"api"
:
api_key
=
os
.
environ
.
get
(
API_KEY_ENV
)
if
not
api_key
or
not
api_key
.
startswith
(
API_KEY_PREFIX
):
# ← 问题在这里
logger
.
error
(
"API模式需要有效的ANTHROPIC_API_KEY环境变量"
)
return
1
``
`
3.
**
前缀常量定义
**
:
``
`
python
# src/config.py
API_KEY_PREFIX
=
"sk-ant-api03-"
# 这个验证太严格了
``
`
## 修复方案
### 方案概述
移除
API
模式下的密钥前缀验证,改为仅检查密钥是否存在且非空。
### 详细实施步骤
#### 步骤1:修改 main.py 的 API 模式验证逻辑
-
[
]
将
`not api_key.startswith(API_KEY_PREFIX)`
的验证移除
-
[
]
改为仅检查
`api_key`
是否存在且非空
#### 步骤2:调整错误提示
-
[
]
更新错误提示信息,不再强调
`sk-ant-api03-`
格式
## 实施细节
### main.py 修改内容
**
修改前
**
:
```
python
if service_mode == "api":
api_key = os.environ.get(API_KEY_ENV)
if not api_key or not api_key.startswith(API_KEY_PREFIX):
logger.error("API模式需要有效的ANTHROPIC_API_KEY环境变量")
logger.error("请设置API密钥:")
logger.error(" Windows: set ANTHROPIC_API_KEY=sk-ant-api03-...")
...
return 1
```
**修改后**:
```
python
if service_mode == "api":
api_key = os.environ.get(API_KEY_ENV)
if not api_key or not api_key.strip():
logger.error("API模式需要ANTHROPIC_API_KEY环境变量")
logger.error("请设置API密钥:")
logger.error(" Windows: set ANTHROPIC_API_KEY=your-key-here")
logger.error(" Linux/Mac: export ANTHROPIC_API_KEY=your-key-here")
logger.error("或在 .env 文件中配置: ANTHROPIC_API_KEY=your-key-here")
return 1
```
### 说明
-
移除
`api_key.startswith(API_KEY_PREFIX)`
验证
-
保留非空检查:
`not api_key or not api_key.strip()`
-
支持任意格式的有效 API 密钥(包括 CCSwitch 格式)
## 优化功能回填
### 已完成的优化
-
[
x
]
移除API密钥前缀验证(main.py第161行)
-
[
x
]
更新错误提示信息(不再强调sk-ant-api03-格式)
-
[
x
]
auto模式逻辑同步修改(移除前缀检查)
### 待完成的优化
-
无
Docs/PRD/AI用例完善/问题处理/生成完善用例完全没有新增内容_问题处理.md
0 → 100644
浏览文件 @
ed7ea006
# 问题描述
## 问题现象
-
在执行代码后生成的完善版用例文件,没有新增内容,差异性报告显示没有任何差异,覆盖率未提升。
-
文件路径:
[
AuxiliaryTool/AIPerfectedTestCases/Perfected/新统一平台权限管理测试用例_完善版本.xlsx
]
、
[
AuxiliaryTool/AIPerfectedTestCases/reports/新统一平台权限管理测试用例_差异性报告.docx
]
# 日志信息
```
ignorelang
PS E:\GithubData\ubains-module-test\AuxiliaryTool\AIPerfectedTestCases> python run.py
============================================================
AI完善测试用例工具 v1.0.0
============================================================
【环境检查】
✓ API密钥: your-api...
✓ 需求文档: 0 份
✓ PRD文档: 7 份
✓ 测试用例文件: 新统一平台权限管理测试用例.xlsx
【项目配置】
项目根目录: E:\GithubData\ubains-module-test\AuxiliaryTool\AIPerfectedTestCases
需求文档目录: E:\GithubData\ubains-module-test\AuxiliaryTool\AIPerfectedTestCases\config\需求文档
PRD文档目录: E:\GithubData\ubains-module-test\AuxiliaryTool\AIPerfectedTestCases\config\开发PRD
测试用例文件: E:\GithubData\ubains-module-test\AuxiliaryTool\AIPerfectedTestCases\config\测试用例\新统一平台权限管理测试用例.xlsx
AI模型: claude-sonnet-4-6
覆盖率目标: 90.0%
【开始处理】
2026-03-10 17:13:44 - src.main - INFO - 日志系统初始化完成,级别=INFO
2026-03-10 17:13:44 - src.main - INFO - ============================================================
2026-03-10 17:13:44 - src.main - INFO - AI完善测试用例工具 启动
2026-03-10 17:13:44 - src.main - INFO - 版本: 1.0.0
2026-03-10 17:13:44 - src.main - INFO - ============================================================
2026-03-10 17:13:44 - src.main - INFO - API密钥已配置: your-api...
2026-03-10 17:13:44 - src.main - INFO - 验证输入文件和目录...
2026-03-10 17:13:44 - src.main - WARNING - 发现以下问题:
2026-03-10 17:13:44 - src.main - WARNING - - 需求文档目录下没有.docx文件: E:\GithubData\ubains-module-test\AuxiliaryTool\AIPerfectedTestCases\config\需求文档
2026-03-10 17:13:44 - src.main - INFO - 输入验证通过
2026-03-10 17:13:44 - src.main - INFO -
2026-03-10 17:13:44 - src.main - INFO - 【步骤1】读取文档
2026-03-10 17:13:44 - src.main - INFO - ------------------------------------------------------------
2026-03-10 17:13:44 - src.document_reader - INFO - 文档读取器初始化完成
2026-03-10 17:13:44 - src.document_reader - INFO - ============================================================
2026-03-10 17:13:44 - src.document_reader - INFO - 开始读取所有文档...
2026-03-10 17:13:44 - src.document_reader - INFO - ============================================================
2026-03-10 17:13:44 - src.document_reader - WARNING - 需求文档目录下没有找到.docx文件: E:\GithubData\ubains-module-test\AuxiliaryTool\AIPerfectedTestCases\config\需求文档
2026-03-10 17:13:44 - src.document_reader - INFO - 找到 7 份PRD文档
2026-03-10 17:13:44 - src.document_reader - INFO - 读取PRD文档: PRD_权限控制_数据扩展补充.md
2026-03-10 17:13:44 - src.document_reader - INFO - 读取PRD文档: PRD_权限控制_权限管理页面新增开发.md
2026-03-10 17:13:44 - src.document_reader - INFO - 读取PRD文档: PRD_权限组管理_基础权限组_调整基础角色基础权限组.md
2026-03-10 17:13:44 - src.document_reader - INFO - 读取PRD文档: PRD_权限组管理_增删改查_权限组功能开发设计.md
2026-03-10 17:13:44 - src.document_reader - INFO - 读取PRD文档: PRD_权限组管理_权限结构_调整权限数据结构.md
2026-03-10 17:13:44 - src.document_reader - INFO - 读取PRD文档: PRD_权限组管理_添加权限组_给基础角色添加基本权限组.md
2026-03-10 17:13:44 - src.document_reader - INFO - 读取PRD文档: PRD_权限组管理_详情查看_添加权限组详情查看.md
2026-03-10 17:13:44 - src.document_reader - INFO - PRD文档读取完成,共 7 份
2026-03-10 17:13:44 - src.document_reader - INFO - 读取测试用例: 新统一平台权限管理测试用例.xlsx
2026-03-10 17:13:44 - src.document_reader - INFO - 测试用例读取完成,共 0 条
2026-03-10 17:13:44 - src.document_reader - INFO - ============================================================
2026-03-10 17:13:44 - src.document_reader - INFO - 所有文档读取完成:
2026-03-10 17:13:44 - src.document_reader - INFO - 需求文档: 0 份
2026-03-10 17:13:44 - src.document_reader - INFO - PRD文档: 7 份
2026-03-10 17:13:44 - src.document_reader - INFO - 测试用例: 0 条
2026-03-10 17:13:44 - src.document_reader - INFO - ============================================================
2026-03-10 17:13:44 - src.main - INFO -
2026-03-10 17:13:44 - src.main - INFO - 【步骤2】分析测试用例质量
2026-03-10 17:13:44 - src.main - INFO - ------------------------------------------------------------
2026-03-10 17:13:44 - src.test_case_analyzer - INFO - 开始分析测试用例质量...
2026-03-10 17:13:44 - src.test_case_analyzer - INFO - 质量分析完成:
2026-03-10 17:13:44 - src.test_case_analyzer - INFO - 总用例数: 0
2026-03-10 17:13:44 - src.test_case_analyzer - INFO - 问题数: 0
2026-03-10 17:13:44 - src.test_case_analyzer - INFO - 质量率: 100.0%
2026-03-10 17:13:44 - src.main - INFO -
2026-03-10 17:13:44 - src.main - INFO - 【步骤3】提取功能点
2026-03-10 17:13:44 - src.main - INFO - ------------------------------------------------------------
2026-03-10 17:13:44 - src.test_case_analyzer - INFO - 测试用例分析器初始化完成,模型: claude-sonnet-4-6
2026-03-10 17:13:44 - src.test_case_analyzer - INFO - 开始提取功能点...
2026-03-10 17:13:45 - httpx - INFO - HTTP Request: POST https://api.anthropic.com/v1/messages "HTTP/1.1 403 Forbidden"
2026-03-10 17:13:45 - src.test_case_analyzer - ERROR - AI API调用失败: Error code: 403 - {'error': {'type': 'forbidden', 'message': 'Request not allowed'}}
2026-03-10 17:13:45 - src.main - WARNING - 未提取到功能点,请检查需求文档内容
2026-03-10 17:13:45 - src.main - INFO -
2026-03-10 17:13:45 - src.main - INFO - 【步骤4】分析覆盖率
2026-03-10 17:13:45 - src.main - INFO - ------------------------------------------------------------
2026-03-10 17:13:45 - src.test_case_analyzer - INFO - 开始分析测试用例覆盖情况...
2026-03-10 17:13:45 - src.test_case_analyzer - INFO - 覆盖率分析完成:
2026-03-10 17:13:45 - src.test_case_analyzer - INFO - 总功能数: 0
2026-03-10 17:13:45 - src.test_case_analyzer - INFO - 已覆盖: 0
2026-03-10 17:13:45 - src.test_case_analyzer - INFO - 未覆盖: 0
2026-03-10 17:13:45 - src.test_case_analyzer - INFO - 覆盖率: 0.0%
2026-03-10 17:13:45 - src.main - INFO -
2026-03-10 17:13:45 - src.main - INFO - 【步骤5】检测缺失
2026-03-10 17:13:45 - src.main - INFO - ------------------------------------------------------------
2026-03-10 17:13:45 - src.gap_detector - INFO - 缺失检测器初始化完成,模型: claude-sonnet-4-6
2026-03-10 17:13:45 - src.gap_detector - INFO - 开始检测测试用例缺失...
2026-03-10 17:13:46 - httpx - INFO - HTTP Request: POST https://api.anthropic.com/v1/messages "HTTP/1.1 403 Forbidden"
2026-03-10 17:13:46 - src.gap_detector - ERROR - AI API调用失败: Error code: 403 - {'error': {'type': 'forbidden', 'message': 'Request not allowed'}}
2026-03-10 17:13:46 - src.gap_detector - INFO - 缺失检测完成:
2026-03-10 17:13:46 - src.gap_detector - INFO - 功能点缺失: 0 项
2026-03-10 17:13:46 - src.gap_detector - INFO - 场景覆盖不足: 0 项
2026-03-10 17:13:46 - src.gap_detector - INFO - 边界条件不足: 0 项
2026-03-10 17:13:46 - src.gap_detector - WARNING - ⚠️ 覆盖率未达标: 0.0% < 90.0%, 差距: 90.0%
2026-03-10 17:13:46 - src.gap_detector - INFO - 估算需新增测试用例: 0 条
2026-03-10 17:13:46 - src.main - INFO -
2026-03-10 17:13:46 - src.main - INFO - 【步骤6】生成新测试用例
2026-03-10 17:13:46 - src.main - INFO - ------------------------------------------------------------
2026-03-10 17:13:46 - src.case_generator - INFO - 测试用例生成器初始化完成,模型: claude-sonnet-4-6
2026-03-10 17:13:46 - src.case_generator - INFO - 开始生成测试用例...
2026-03-10 17:13:46 - src.case_generator - INFO - 为 0 个功能点缺失生成测试用例...
2026-03-10 17:13:46 - src.case_generator - INFO - 功能点测试用例生成完成: 0 条
2026-03-10 17:13:46 - src.case_generator - INFO - 为 0 个场景覆盖不足生成测试用例...
2026-03-10 17:13:46 - src.case_generator - INFO - 场景测试用例生成完成: 0 条
2026-03-10 17:13:46 - src.case_generator - INFO - 为 0 个边界条件不足生成测试用例...
2026-03-10 17:13:46 - src.case_generator - INFO - 边界条件测试用例生成完成: 0 条
2026-03-10 17:13:46 - src.case_generator - INFO - 测试用例生成完成,共 0 条
2026-03-10 17:13:46 - src.main - INFO -
2026-03-10 17:13:46 - src.main - INFO - 【步骤7】去重处理
2026-03-10 17:13:46 - src.main - INFO - ------------------------------------------------------------
2026-03-10 17:13:46 - src.deduplicator - INFO - 测试用例去重器初始化完成
2026-03-10 17:13:46 - src.deduplicator - INFO - 开始去重处理...
2026-03-10 17:13:46 - src.deduplicator - INFO - 现有用例: 0 条,新生成用例: 0 条
2026-03-10 17:13:46 - src.deduplicator - INFO - 去重完成:
2026-03-10 17:13:46 - src.deduplicator - INFO - 唯一新用例: 0 条
2026-03-10 17:13:46 - src.deduplicator - INFO - 与现有用例重复: 0 条
2026-03-10 17:13:46 - src.deduplicator - INFO - 新用例内部重复: 0 条
2026-03-10 17:13:46 - src.deduplicator - INFO - 总计重复: 0 条
2026-03-10 17:13:46 - src.deduplicator - INFO - 去重率: 0.0%
2026-03-10 17:13:46 - src.main - INFO -
2026-03-10 17:13:46 - src.main - INFO - 【步骤8】保存完善后的测试用例
2026-03-10 17:13:46 - src.main - INFO - ------------------------------------------------------------
2026-03-10 17:13:46 - src.main - INFO - 正在保存完善后的测试用例...
2026-03-10 17:13:46 - src.main - INFO - 完善后的测试用例已保存: E:\GithubData\ubains-module-test\AuxiliaryTool\AIPerfectedTestCases\Perfected\新统一平台权限管理测试用例_完善版本.xlsx
2026-03-10 17:13:46 - src.main - INFO - 原有用例: 0 条
2026-03-10 17:13:46 - src.main - INFO - 新增用例: 0 条
2026-03-10 17:13:46 - src.main - INFO - 总计: 0 条
2026-03-10 17:13:46 - src.main - INFO -
2026-03-10 17:13:46 - src.main - INFO - 【步骤9】生成差异性报告
2026-03-10 17:13:46 - src.main - INFO - ------------------------------------------------------------
2026-03-10 17:13:46 - src.report_generator - INFO - 报告生成器初始化完成
2026-03-10 17:13:46 - src.report_generator - INFO - 开始生成差异性报告: E:\GithubData\ubains-module-test\AuxiliaryTool\AIPerfectedTestCases\reports\新统一平台权限管理测试用例_差异性报告.docx
2026-03-10 17:13:46 - src.report_generator - INFO - 报告生成完成: E:\GithubData\ubains-module-test\AuxiliaryTool\AIPerfectedTestCases\reports\新统一平台权限管理测试用例_差异性报告.docx
2026-03-10 17:13:46 - src.main - INFO -
2026-03-10 17:13:46 - src.main - INFO - ============================================================
2026-03-10 17:13:46 - src.main - INFO - 处理完成!
2026-03-10 17:13:46 - src.main - INFO - ============================================================
2026-03-10 17:13:46 - src.main - INFO - 【输入】
2026-03-10 17:13:46 - src.main - INFO - 需求文档: 0 份
2026-03-10 17:13:46 - src.main - INFO - PRD文档: 7 份
2026-03-10 17:13:46 - src.main - INFO - 原始用例: 0 条
2026-03-10 17:13:46 - src.main - INFO -
2026-03-10 17:13:46 - src.main - INFO - 【输出】
2026-03-10 17:13:46 - src.main - INFO - 完善后的测试用例: E:\GithubData\ubains-module-test\AuxiliaryTool\AIPerfectedTestCases\Perfected\新统一平台权限管理测试用例_完善版本.xlsx
2026-03-10 17:13:46 - src.main - INFO - 差异性报告: E:\GithubData\ubains-module-test\AuxiliaryTool\AIPerfectedTestCases\reports\新统一平台权限管理测试用例_差异性报告.docx
2026-03-10 17:13:46 - src.main - INFO -
2026-03-10 17:13:46 - src.main - INFO - 【统计】
2026-03-10 17:13:46 - src.main - INFO - 覆盖率: 0.0% -> 20.0% (预计)
2026-03-10 17:13:46 - src.main - INFO - 新增用例: 0 条(去重后)
2026-03-10 17:13:46 - src.main - INFO - 功能点缺失: 0 项
2026-03-10 17:13:46 - src.main - INFO - 场景缺失: 0 项
2026-03-10 17:13:46 - src.main - INFO - 边界缺失: 0 项
2026-03-10 17:13:46 - src.main - INFO - ============================================================
```
\ No newline at end of file
Docs/PRD/AI用例完善/问题处理/生成完善用例完全没有新增内容_问题处理_计划执行.md
0 → 100644
浏览文件 @
ed7ea006
# 生成完善用例完全没有新增内容_问题处理_计划执行
## 1. 问题描述
### 问题现象
执行代码后生成的完善版用例文件没有新增内容,差异性报告显示没有任何差异,覆盖率未提升。
### 问题文件
-
完善用例:
`AuxiliaryTool/AIPerfectedTestCases/Perfected/新统一平台权限管理测试用例_完善版本.xlsx`
-
差异性报告:
`AuxiliaryTool/AIPerfectedTestCases/reports/新统一平台权限管理测试用例_差异性报告.docx`
---
## 2. 问题分析
### 2.1 核心问题分析
根据日志信息,存在以下问题:
| 问题 | 日志证据 | 影响 |
|------|----------|------|
| 需求文档为空 |
`需求文档: 0 份`
| 无需求文档供AI分析 |
| 测试用例为空 |
`测试用例读取完成,共 0 条`
| 无法分析现有覆盖 |
| API调用失败 |
`HTTP/1.1 403 Forbidden`
| 无法使用AI提取功能点 |
| 功能点为空 |
`总功能数: 0`
| 无法检测缺失和生成用例 |
### 2.2 根本原因
#### 原因1:API密钥问题(403 Forbidden)
```
HTTP Request: POST https://api.anthropic.com/v1/messages "HTTP/1.1 403 Forbidden"
Error code: 403 - {'error': {'type': 'forbidden', 'message': 'Request not allowed'}}
```
**可能原因**
:
-
API密钥无效或过期
-
API密钥格式错误
-
API密钥没有调用权限
-
账户余额不足
#### 原因2:需求文档目录为空
```
需求文档目录下没有找到.docx文件: ...config\需求文档
```
**影响**
:没有需求文档,AI无法提取功能点列表。
#### 原因3:测试用例读取为0条
```
测试用例读取完成,共 0 条
```
**可能原因**
:
-
Excel文件格式不符合预期
-
表头行不在第一行
-
数据行被跳过
---
## 3. 解决方案
### 3.1 API密钥问题修复
#### 方案A:验证并更新API密钥
1.
**验证API密钥是否有效**
-
访问 Anthropic 控制台:https://console.anthropic.com/
-
检查API密钥是否有效
-
检查账户余额和使用情况
2.
**更新API密钥**
-
更新
`.env`
文件中的
`ANTHROPIC_API_KEY`
-
确保密钥格式正确:
`sk-ant-api03-xxxxxxxxx`
3.
**测试API连接**
```
bash
# 添加测试命令
python run.py
--test-api
```
#### 方案B:添加API测试功能
在
`
run.py
`
中添加API测试命令:
```
python
def test_api_connection(api_key: str) -> bool:
"""测试API连接"""
try:
import anthropic
client = anthropic.Anthropic(api_key=api_key)
response = client.messages.create(
model="claude-sonnet-4-6",
max_tokens=10,
messages=
[
{"role": "user", "content": "Hello"}
]
)
print("✓ API连接正常")
return True
except Exception as e:
print(f"✗ API连接失败: {e}")
return False
```
### 3.2 需求文档问题修复
#### 方案A:添加需求文档
将需求文档(.docx格式)放入 `config/需求文档/` 目录。
#### 方案B:支持从PRD文档提取功能点
修改代码,使AI可以从PRD文档中提取功能点(无需需求文档)。
```
python
def extract_function_points_from_prd(self, prds: List
[
Dict
]
) -> List
[
Dict
]
:
"""仅从PRD文档提取功能点"""
prompt = """请分析以下PRD文档,提取完整的功能点列表。
...
"""
```
### 3.3 测试用例读取问题修复
#### 方案A:增强Excel读取兼容性
修改 `document_reader.py`,增加错误处理和调试信息:
```
python
def read_test_cases(self, test_case_file: Path) -> List
[
Dict
]
:
"""读取测试用例(.xlsx)"""
logger.info("读取测试用例: %s", test_case_file.name)
try:
wb = openpyxl.load_workbook(test_case_file)
# 打印所有sheet名称
logger.debug("工作表列表: %s", wb.sheetnames)
# 使用第一个工作表
ws = wb.active
logger.debug("使用工作表: %s", ws.title)
logger.debug("工作表大小: %d 行 x %d 列", ws.max_row, ws.max_column)
# 读取表头
headers = [cell.value for cell in ws[1]]
logger.debug("表头: %s", headers)
cases = []
empty_rows = 0
for row_idx, row in enumerate(ws.iter_rows(min_row=2, values_only=True), start=2):
# 跳过全空行
if not any(row):
empty_rows += 1
continue
case = dict(zip(headers, row))
# 检查是否有用例名称
case_name = case.get("用例名称")
if case_name and str(case_name).strip():
case["_row_number"] = row_idx
cases.append(case)
else:
logger.debug("第%d行:用例名称为空,跳过", row_idx)
logger.info("测试用例读取完成,共 %d 条(跳过 %d 个空行)",
len(cases), empty_rows)
return cases
except Exception as e:
logger.error("读取测试用例失败: %s", e)
raise
```
#### 方案B:支持指定工作表
添加参数支持指定使用哪个工作表:
```
python
def read_test_cases(self, test_case_file: Path, sheet_name: str = None) -> List
[
Dict
]
:
"""读取测试用例(.xlsx)"""
wb = openpyxl.load_workbook(test_case_file)
if sheet_name:
ws = wb[sheet_name]
else:
ws = wb.active
```
---
## 4. 实施计划
### 4.1 代码修改
- [ ] 添加API测试功能
- [ ] 修改功能点提取逻辑,支持仅从PRD提取
- [ ] 增强Excel读取错误处理和调试信息
- [ ] 添加详细日志便于排查问题
### 4.2 配置检查
- [ ] 验证API密钥是否有效
- [ ] 检查需求文档目录
- [ ] 检查测试用例文件格式
### 4.3 测试验证
- [ ] 测试API连接
- [ ] 测试PRD文档提取功能点
- [ ] 测试测试用例读取
---
## 5. 代码实现
### 5.1 添加API测试功能
**文件:`run.py`**
```
python
def test_api_command() -> int:
"""测试API连接"""
print("【测试API连接】")
print()
api_key = os.environ.get(API_KEY_ENV)
if not api_key:
print("✗ 未设置API密钥")
print(f" 请设置环境变量: {API_KEY_ENV}")
print(" 或在 .env 文件中配置")
return 1
# 隐藏密钥显示
masked_key = api_key[:8] + "..." if len(api_key) > 8 else "***"
print(f"API密钥: {masked_key}")
print()
try:
import anthropic
client = anthropic.Anthropic(api_key=api_key)
print("发送测试请求...")
response = client.messages.create(
model="claude-sonnet-4-6",
max_tokens=50,
messages=[{"role": "user", "content": "请回复:API连接成功"}]
)
result = response.content[0].text
print(f"✓ API连接正常")
print(f" AI回复: {result}")
return 0
except anthropic.AuthenticationError:
print("✗ API密钥认证失败")
print(" 请检查API密钥是否正确")
return 1
except anthropic.PermissionDeniedError:
print("✗ API权限不足")
print(" 请检查API密钥是否有调用权限")
return 1
except anthropic.RateLimitError:
print("✗ API调用频率超限")
print(" 请稍后再试")
return 1
except Exception as e:
print(f"✗ API连接失败: {e}")
return 1
```
添加命令行参数:
```
python
parser.add_argument(
'--test-api',
action='store_true',
dest='test_api',
help='测试API连接'
)
```
### 5.2 修改功能点提取逻辑
**文件:`src/test_case_analyzer.py`**
```
python
def extract_function_points(self, requirements: List
[
Dict
]
,
prds: List
[
Dict
]
) -> List
[
Dict
]
:
"""
使用AI提取功能点列表
优先从需求文档提取,如果没有需求文档则从PRD文档提取
"""
logger.info("开始提取功能点...")
# 检查是否有需求文档
has_requirements = len(requirements) > 0
if has_requirements:
logger.info("使用需求文档和PRD文档提取功能点")
prompt = self._build_extract_prompt(requirements, prds)
else:
logger.warning("没有需求文档,仅使用PRD文档提取功能点")
prompt = self._build_prd_only_prompt(prds)
# 调用AI...
```
添加仅从PRD提取的提示词:
```
python
def _build_prd_only_prompt(self, prds: List
[
Dict
]
) -> str:
"""构建仅从PRD提取功能点的提示词"""
prompt = """你是一个专业的测试用例分析专家。请分析以下PRD文档,提取完整的功能点列表。
【要求】
1.
按功能模块分类
2.
每个功能点包含:功能模块、功能编号、功能描述
3.
识别所有子功能
4.
输出格式必须是JSON数组
【开发PRD文档】
"""
for prd in prds:
prompt += f"\n--- {prd['filename']} ---\n"
content = prd['content']
if len(content) > 5000:
content = content[:5000] + "\n...(内容已截断)"
prompt += content + "\n"
prompt += """
\n
请输出JSON格式的功能点列表。
注意:必须输出合法的JSON格式,只输出JSON数组,不要有其他说明文字。
"""
return prompt
```
### 5.3 增强Excel读取
**文件:`src/document_reader.py`**
修改 `read_test_cases` 方法,增加更多调试信息:
```
python
def read_test_cases(self, test_case_file: Path) -> List
[
Dict
]
:
"""读取测试用例(.xlsx)"""
logger.info("读取测试用例: %s", test_case_file.name)
if not test_case_file.exists():
logger.error("测试用例文件不存在: %s", test_case_file)
raise FileNotFoundError(f"测试用例文件不存在: {test_case_file}")
try:
wb = openpyxl.load_workbook(test_case_file, read_only=True, data_only=True)
# 打印工作表信息
logger.info("工作表列表: %s", wb.sheetnames)
# 使用活动工作表
ws = wb.active
logger.info("使用工作表: %s (大小: %d行 x %d列)",
ws.title, ws.max_row, ws.max_column)
# 读取表头
headers = None
for row_idx in range(1, min(10, ws.max_row + 1)):
row_data = [cell.value for cell in ws[row_idx]]
if any("用例名称" in str(cell.value) for cell in ws[row_idx]):
headers = row_data
logger.info("在第%d行找到表头: %s", row_idx, headers)
break
if not headers:
logger.warning("未找到包含'用例名称'的表头行,使用第1行作为表头")
headers = [cell.value for cell in ws[1]]
# 读取数据
cases = []
data_start_row = 2 # 默认从第2行开始
for row_idx in range(data_start_row, ws.max_row + 1):
row_data = [cell.value for cell in ws[row_idx]]
# 跳过空行
if not any(row_data):
continue
case = dict(zip(headers, row_data))
# 检查是否有有效内容
case_name = case.get("用例名称")
if case_name and str(case_name).strip():
case["_row_number"] = row_idx
cases.append(case)
elif row_idx < 10: # 只打印前10行的调试信息
logger.debug("第%d行:用例名称为空或不存在,跳过。行数据: %s",
row_idx, {k: v for k, v in case.items() if v})
logger.info("测试用例读取完成,共 %d 条", len(cases))
wb.close()
return cases
except Exception as e:
logger.error("读取测试用例失败: %s", e)
import traceback
logger.debug(traceback.format_exc())
raise
```
---
## 6. 实施状态
| 项目 | 状态 |
|------|------|
| 问题分析 | ✅ 完成 |
| 解决方案设计 | ✅ 完成 |
| 代码实现 | ⏳ 待实施 |
| 测试验证 | ⏳ 待完成 |
---
## 7. 验证清单
-
[
]
API密钥有效,连接测试通过
-
[
]
需求文档已添加或支持仅从PRD提取
-
[
]
测试用例能正确读取
-
[
]
功能点能正确提取
-
[
]
缺失检测能正常工作
-
[
]
新用例能正常生成
-
[
]
差异性报告正确生成
---
## 8. 注意事项
1.
**API密钥安全**
:不要将API密钥提交到代码仓库
2.
**文件格式**
:确保测试用例Excel文件格式正确
3.
**日志级别**
:使用
`--log-level DEBUG`
查看详细日志
4.
**网络连接**
:确保能访问 Anthropic API
Docs/PRD/AI用例完善/问题处理/生成测试用例失败_问题处理.md
0 → 100644
浏览文件 @
ed7ea006
# 问题描述
## 问题现象
-
运行代码后存在
`ERROR - 生成测试用例失败(权限数据结构调整后的分类层级展示): 'list' object has no attribute 'get'`
报错信息。
# 日志信息
```
ignorelang
2026-03-10 18:14:32 - src.main - INFO -
2026-03-10 18:14:32 - src.main - INFO - 【步骤6】生成新测试用例
2026-03-10 18:14:32 - src.main - INFO - ------------------------------------------------------------
2026-03-10 18:14:32 - src.ai_service_manager - INFO - 使用自定义API endpoint: https://open.bigmodel.cn/api/anthropic
2026-03-10 18:14:32 - src.ai_service_manager - INFO - Anthropic API模式已启用
2026-03-10 18:14:32 - src.ai_service_manager - INFO - AI服务管理器初始化完成,模式: api
2026-03-10 18:14:32 - src.case_generator - INFO - 测试用例生成器初始化完成
2026-03-10 18:14:32 - src.case_generator - INFO - 开始生成测试用例...
2026-03-10 18:14:32 - src.case_generator - INFO - 为 6 个功能点缺失生成测试用例...
2026-03-10 18:14:36 - httpx - INFO - HTTP Request: POST https://open.bigmodel.cn/api/anthropic/v1/messages "HTTP/1.1 200 OK"
2026-03-10 18:14:39 - httpx - INFO - HTTP Request: POST https://open.bigmodel.cn/api/anthropic/v1/messages "HTTP/1.1 200 OK"
2026-03-10 18:14:42 - httpx - INFO - HTTP Request: POST https://open.bigmodel.cn/api/anthropic/v1/messages "HTTP/1.1 200 OK"
2026-03-10 18:14:49 - httpx - INFO - HTTP Request: POST https://open.bigmodel.cn/api/anthropic/v1/messages "HTTP/1.1 200 OK"
2026-03-10 18:14:54 - httpx - INFO - HTTP Request: POST https://open.bigmodel.cn/api/anthropic/v1/messages "HTTP/1.1 200 OK"
2026-03-10 18:14:57 - httpx - INFO - HTTP Request: POST https://open.bigmodel.cn/api/anthropic/v1/messages "HTTP/1.1 200 OK"
2026-03-10 18:14:57 - src.case_generator - ERROR - 生成测试用例失败(权限数据结构调整后的分类层级展示): 'list' object has no attribute 'get'
2026-03-10 18:14:57 - src.case_generator - INFO - 功能点测试用例生成完成: 5 条
2026-03-10 18:14:57 - src.case_generator - INFO - 为 5 个场景覆盖不足生成测试用例...
2026-03-10 18:15:03 - httpx - INFO - HTTP Request: POST https://open.bigmodel.cn/api/anthropic/v1/messages "HTTP/1.1 200 OK"
2026-03-10 18:15:06 - httpx - INFO - HTTP Request: POST https://open.bigmodel.cn/api/anthropic/v1/messages "HTTP/1.1 200 OK"
2026-03-10 18:15:13 - httpx - INFO - HTTP Request: POST https://open.bigmodel.cn/api/anthropic/v1/messages "HTTP/1.1 200 OK"
2026-03-10 18:15:18 - httpx - INFO - HTTP Request: POST https://open.bigmodel.cn/api/anthropic/v1/messages "HTTP/1.1 200 OK"
2026-03-10 18:15:22 - httpx - INFO - HTTP Request: POST https://open.bigmodel.cn/api/anthropic/v1/messages "HTTP/1.1 200 OK"
2026-03-10 18:15:22 - src.case_generator - INFO - 场景测试用例生成完成: 5 条
2026-03-10 18:15:22 - src.case_generator - INFO - 为 5 个边界条件不足生成测试用例...
2026-03-10 18:15:25 - httpx - INFO - HTTP Request: POST https://open.bigmodel.cn/api/anthropic/v1/messages "HTTP/1.1 200 OK"
2026-03-10 18:15:29 - httpx - INFO - HTTP Request: POST https://open.bigmodel.cn/api/anthropic/v1/messages "HTTP/1.1 200 OK"
2026-03-10 18:15:33 - httpx - INFO - HTTP Request: POST https://open.bigmodel.cn/api/anthropic/v1/messages "HTTP/1.1 200 OK"
2026-03-10 18:15:38 - httpx - INFO - HTTP Request: POST https://open.bigmodel.cn/api/anthropic/v1/messages "HTTP/1.1 200 OK"
2026-03-10 18:15:42 - httpx - INFO - HTTP Request: POST https://open.bigmodel.cn/api/anthropic/v1/messages "HTTP/1.1 200 OK"
2026-03-10 18:15:42 - src.case_generator - INFO - 边界条件测试用例生成完成: 5 条
2026-03-10 18:15:42 - src.case_generator - INFO - 测试用例生成完成,共 15 条
```
Docs/PRD/AI用例完善/问题处理/生成测试用例失败_问题处理_计划执行.md
0 → 100644
浏览文件 @
ed7ea006
# 生成测试用例失败_问题处理_计划执行
## 问题分析
### 问题现象
运行代码时报错:
`'list' object has no attribute 'get'`
### 日志信息
```
2026-03-10 18:14:57 - src.case_generator - ERROR - 生成测试用例失败(权限数据结构调整后的分类层级展示): 'list' object has no attribute 'get'
```
### 根本原因
**问题流程**
:
1.
`case_generator.py`
调用
`self.ai_service.ask_json(prompt)`
期望获取单个测试用例的字典对象
2.
`ai_service_manager.py`
的
`_parse_json()`
方法声明返回
`Dict`
,但实际上直接返回
`json.loads()`
的结果
3.
当智谱AI返回的是一个JSON数组(如
`[{用例1}, {用例2}]`
)时,
`json.loads()`
返回的是
`list`
而不是
`dict`
4.
`case_generator.py`
在第313行调用
`case.get("测试频次")`
时,由于
`case`
是
`list`
对象,list没有
`.get()`
方法,导致报错
**问题代码**
:
```
python
# ai_service_manager.py - _parse_json
def
_parse_json
(
self
,
text
:
str
)
->
Dict
:
...
return
json
.
loads
(
json_str
)
# ❌ 如果JSON是数组,返回的是list
```
**智谱AI的返回格式可能与Claude不同**
:
-
Claude通常按提示词要求返回单个对象
-
智谱AI可能返回包含多个对象的数组
## 修复方案
### 方案概述
1.
修改
`_parse_json`
方法,处理返回数组的情况
2.
在
`case_generator.py`
的
`_call_ai_for_case`
中增强类型检查和处理
### 实施细节
#### 修改1:ai_service_manager.py - _parse_json 方法
当解析结果是数组时:
-
如果数组只有一个元素且是对象,返回该对象
-
如果数组有多个元素,记录警告并返回第一个对象
-
这样可以兼容不同的AI返回格式
```
python
def
_parse_json
(
self
,
text
:
str
)
->
Dict
:
"""解析JSON文本"""
import
json
logger
.
debug
(
"尝试解析JSON,内容长度:
%
d"
,
len
(
text
))
json_str
=
self
.
_extract_json
(
text
)
if
json_str
:
try
:
result
=
json
.
loads
(
json_str
)
# 如果返回的是数组,取第一个元素
if
isinstance
(
result
,
list
):
if
len
(
result
)
>
0
:
if
isinstance
(
result
[
0
],
dict
):
logger
.
info
(
"JSON解析结果是数组,取第一个元素"
)
return
result
[
0
]
else
:
logger
.
warning
(
"JSON数组第一个元素不是对象"
)
else
:
logger
.
warning
(
"JSON数组为空"
)
raise
ValueError
(
"JSON解析结果为空数组"
)
return
result
except
json
.
JSONDecodeError
as
e
:
logger
.
error
(
"JSON解析失败:
%
s"
,
e
)
logger
.
debug
(
"提取的JSON字符串:
%
s"
,
json_str
[:
500
])
raise
ValueError
(
f
"无法解析JSON: {text[:200]}"
)
```
#### 修改2:case_generator.py - _call_ai_for_case 方法
增加类型检查,确保返回的是字典对象:
```
python
def
_call_ai_for_case
(
self
,
prompt
:
str
,
description
:
str
)
->
Optional
[
Dict
]:
"""调用AI生成单个测试用例"""
try
:
case
=
self
.
ai_service
.
ask_json
(
prompt
,
AI_MODEL
)
# 类型检查:确保返回的是字典
if
isinstance
(
case
,
list
):
if
len
(
case
)
>
0
and
isinstance
(
case
[
0
],
dict
):
logger
.
info
(
"AI返回了数组,取第一个元素"
)
case
=
case
[
0
]
else
:
logger
.
error
(
"AI返回的数组格式不符合预期"
)
return
None
if
not
isinstance
(
case
,
dict
):
logger
.
error
(
"AI返回的不是字典对象:
%
s"
,
type
(
case
))
return
None
# 设置默认值
if
not
case
.
get
(
"测试频次"
):
case
[
"测试频次"
]
=
DEFAULT_TEST_FREQUENCY
if
not
case
.
get
(
"JSON"
):
case
[
"JSON"
]
=
"暂不涉及"
return
case
except
Exception
as
e
:
logger
.
error
(
"生成测试用例失败(
%
s):
%
s"
,
description
,
e
)
logger
.
debug
(
"错误详情"
,
exc_info
=
True
)
return
None
```
## 优化功能回填
### 已完成的优化
-
[
x
]
修改 ai_service_manager.py 的 _parse_json 方法(处理数组返回情况)
-
[
x
]
修改 case_generator.py 的 _call_ai_for_case 方法(增加类型检查)
### 待完成的优化
-
无
### 修复说明
智谱AI等模型可能返回JSON数组而不是单个对象。修改后的代码:
1.
`_parse_json`
方法:当解析结果是数组时,自动取第一个元素
2.
`_call_ai_for_case`
方法:增加类型检查,兼容数组返回格式
3.
添加详细的调试日志,便于排查问题
Docs/PRD/AI用例完善/需求文档/AI服务调用方式补充说明.md
0 → 100644
浏览文件 @
ed7ea006
# AI服务调用方式补充说明
## 概述
AI完善测试用例工具支持两种AI调用方式,用户可根据实际情况选择:
---
## 方式对比
| 方式 | 需求 | 费用 | 速度 | 稳定性 |
|------|------|------|------|--------|
|
**Anthropic API**
| API密钥 | 按量计费 | 快 | 高 |
|
**Claude Code CLI**
| Claude CLI工具 | 免费 | 较慢 | 中 |
---
## 方式A:Anthropic API(推荐)
### 说明
直接通过Python代码调用Anthropic官方API,需要获取API密钥。
### 获取方式
1.
访问 https://console.anthropic.com/
2.
创建API密钥
3.
配置到
`.env`
文件
### 费用
-
Claude Sonnet 4.6:$3 / 1M input tokens
-
Claude Opus 4.6:$15 / 1M input tokens
-
预估成本:处理100个测试用例约 $0.05-0.15
### 优点
-
✅ 性能最好,响应最快
-
✅ 功能最完整
-
✅ 可监控使用量和费用
---
## 方式B:Claude Code CLI(备选)
### 说明
通过调用本地的
`claude`
命令行工具来使用AI能力,使用现有的Session Token。
### 工作原理
```
程序运行 → 自动调用claude命令 → 获取结果 → 命令退出
↑ ↓
└─────────────── 无需手动操作,自动拉起 ─────────┘
```
### 前置条件
1.
已安装 Claude Code CLI 工具
2.
`claude`
命令在系统 PATH 中可用
3.
已登录过一次(Session Token已保存)
### 配置方式
**自动检测**
:程序会自动检测API密钥格式,如果不满足要求则自动切换到CLI模式。
**手动指定**
(可选):
```
bash
python run.py
--use-cli
```
### 费用
-
✅ 免费(使用现有Session Token)
### 优点
-
✅ 无需额外获取API密钥
-
✅ 无额外API费用
-
✅ 配置简单
### 缺点
-
⚠️ 需要系统已安装Claude Code CLI
-
⚠️ 调用速度较直接API慢
-
⚠️ 依赖外部工具
---
## 自动切换逻辑
程序启动时会按以下逻辑自动选择:
```
1. 检查环境变量ANTHROPIC_API_KEY
│
2. 判断密钥格式
│
├─ 以 "sk-ant-api03-" 开头?
│ └─ 是 → 使用 Anthropic API 模式
│
└─ 否 → 检查claude命令是否可用
└─ 可用 → 使用 Claude CLI 模式
```
---
## 使用建议
### 推荐使用 Anthropic API 的场景
-
需要处理大量测试用例(>100个)
-
对处理速度有要求
-
需要精确监控使用量
-
已有API使用环境
### 推荐使用 Claude CLI 的场景
-
测试验证阶段
-
用例数量较少(<50个)
-
无额外API预算
-
已习惯使用Claude Code CLI
---
## 切换方式
### 从CLI切换到API
1.
获取 Anthropic API 密钥
2.
更新
`.env`
文件:
```
ANTHROPIC_API_KEY=sk-ant-api03-xxxxx
```
3.
重新运行程序
### 从API切换到CLI
1.
清空或注释
`.env`
中的 API 密钥
2.
确认
`claude`
命令可用
3.
重新运行程序
Docs/PRD/AI用例完善/需求文档/AI用例完善需求文档.md
0 → 100644
浏览文件 @
ed7ea006
# AI完善用例需求文档
## 代码路径
-
代码路径:
[
AuxiliaryTool/AIPerfectedTestCases
]
## 功能需求
### 功能目标
**目标:**
通过结合已有测试用例+需求文档+开发需求PRD文档,对当前已有测试用例进行查漏补缺,完善测试用例内容,确保测试用例覆盖全面且符合规范要求。
### 需求描述
#### 文档路径获取
-
文档路径:
[
AuxiliaryTool/AIPerfectedTestCases/config
]
-
需求文档:
`config/需求文档/*.docx`
-
开发需求PRD文档:
`config/开发PRD/*.md`
-
测试用例:
`config/测试用例/新统一平台权限管理测试用例.xlsx`
#### 测试用例格式确认
-
测试用例格式:Excel表格,包含以下列:
-
序列号:序号,用于排序
-
功能模块:功能模块名称,用于分类
-
用例编号:用例编号,用于标识用例
-
功能描述:功能描述,用于描述功能
-
用例等级:用例等级,用于描述用例的优先级
-
功能编号:功能编号,用于标识功能
-
用例名称:用例名称,用于标识用例
-
预置条件:测试用例的预置条件,用于准备测试环境
-
STEP:步骤描述
-
JSON:自动化测试用例,暂不涉及
-
预期结果:测试用例的预期结果,用于描述测试结果
-
测试结果:测试用例的测试结果,用于描述测试结果
-
测试频次:测试用例的测试频次,用于描述测试用例的测试频次
-
日志/截图/照片:用于记录测试过程中产生的日志、截图、照片,用于测试结果分析。
-
备注:用于记录测试过程中产生的问题,用于问题排查。
#### 功能实现方式
-
AI服务:通过AI服务调用,对需求文档和开发需求PRD文档进行解析和理解,提取功能点,并生成测试用例内容。
-
API模式:通过兼容Anthropic API进行调用:
-
支持官方Anthropic API
-
支持通过CCSwitch适配的智谱AI(GLM)等服务
-
配置项:ANTHROPIC_API_KEY、API_BASE_URL
-
CLT模式:通过本地claude命令行工具调用,使用现有session token
-
安装: npm install -g @anthropic/claude
-
Auto模式:自动检测,优先使用API模式,若API模式不可用则使用CLT模式。
-
需求认知:阅读需求文档中功能描述,确定功能需求。
-
获取测试用例:从测试用例文件中获取当前已有的测试用例内容。
-
读取开发需求PRD文档:从开发需求PRD文档中获取功能实现细节和边界条件。
-
目录位置:
`AuxiliaryTool/AIPerfectedTestCases/config/开发PRD`
,且存在多份PRD文档,需对每份文档进行读取和分析。
-
查漏补缺:通过对比需求文档和开发需求PRD文档,将缺少的测试用例添加到测试用例文件中。
-
功能点缺失:基于读取需求文档和开发需求PRD文档,比较测试用例中缺少的测试点。
-
场景覆盖不足:基于需求文档和开发需求PRD文档,比较测试用例中缺少的场景,覆盖率目标为90%。
-
边界条件不足:基于需求文档和开发需求PRD文档,比较测试用例中缺少的边界条件。
-
去重处理:对添加的测试用例进行去重,确保测试用例不重复。
-
完善测试用例:将需求文档和开发需求PRD文档中的内容,与已有的测试用例进行对比,并添加缺失的测试用例。
-
生成完善后的新用例文件,不直接修改原文件,添加到
`AuxiliaryTool/AIPerfectedTestCases/Perfected`
目录下,命名为
`新统一平台权限管理测试用例_完善版本.xlsx`
-
生成差异性报告,将差异性报告添加到
`AuxiliaryTool/AIPerfectedTestCases/reports`
目录下,命名为
`新统一平台权限管理测试用例_差异性报告.docx`
,报告内容包括:
-
功能点缺失的测试用例列表
-
场景覆盖不足的测试用例列表
-
边界条件不足的测试用例列表
-
去重处理结果
-
用例数量增长统计
-
覆盖率百分比统计
## 规范文档
-
代码规范:
`Docs/PRD/01规范文档/_PRD_规范文档_代码规范.md`
-
问题总结:
`Docs/PRD/01规范文档/_PRD_问题总结_记录文档.md`
-
方法总结:
`Docs/PRD/01规范文档/_PRD_方法总结_记录文档.md`
-
文档规范:
`Docs/PRD/01规范文档/_PRD_规范文档_文档规范.md`
-
测试规范:
`Docs/PRD/01规范文档/_PRD_规范文档_测试规范.md`
---
\ No newline at end of file
Docs/PRD/AI用例完善/需求文档/AI用例完善需求文档_计划执行.md
0 → 100644
浏览文件 @
ed7ea006
# AI完善用例需求文档_计划执行
## 1. 项目概述
### 1.1 项目背景
通过结合已有测试用例、需求文档、开发需求PRD文档,利用AI能力对测试用例进行查漏补缺,完善测试用例内容,确保测试用例覆盖全面且符合规范要求。
### 1.2 项目目标
-
自动读取需求文档和开发PRD文档
-
分析现有测试用例的覆盖情况
-
识别缺失的功能点、场景、边界条件
-
生成完善的测试用例
-
输出差异性报告
### 1.3 覆盖率目标
-
场景覆盖率目标:
**90%**
---
## 2. 项目结构
```
AuxiliaryTool/AIPerfectedTestCases/
├── src/ # 源代码目录
│ ├── __init__.py
│ ├── config.py # 配置模块
│ ├── document_reader.py # 文档读取模块
│ ├── test_case_analyzer.py # 测试用例分析模块
│ ├── gap_detector.py # 缺失检测模块
│ ├── case_generator.py # 用例生成模块
│ ├── deduplicator.py # 去重模块
│ ├── report_generator.py # 报告生成模块
│ └── main.py # 主入口模块
├── config/ # 配置文件目录
│ ├── 需求文档/
│ │ └── *.docx # 需求文档
│ ├── 开发PRD/
│ │ └── *.md # 开发PRD文档
│ └── 测试用例/
│ └── 新统一平台权限管理测试用例.xlsx
├── Perfected/ # 输出目录
│ └── 新统一平台权限管理测试用例_完善版本.xlsx
├── reports/ # 报告输出目录
│ └── 新统一平台权限管理测试用例_差异性报告.docx
├── logs/ # 日志目录
├── run.py # 入口脚本
├── requirements.txt # 依赖包列表
└── README.md # 说明文档
```
---
## 3. 技术方案
### 3.1 技术栈
| 技术 | 用途 |
|------|------|
| Python 3.10+ | 开发语言 |
| openpyxl | Excel文件读写 |
| python-docx | Word文档读写 |
| anthropic | Claude API SDK |
| pandas | 数据处理 |
### 3.2 AI服务调用方式
本工具支持多种AI服务调用方式,可灵活配置:
#### API模式(推荐)
通过兼容Anthropic API格式的服务调用:
-
支持官方Anthropic API
-
支持通过CCSwitch适配的智谱AI(GLM)等服务
-
配置项:
`ANTHROPIC_API_KEY`
、
`API_BASE_URL`
、
`AI_MODEL`
```
python
import
anthropic
# API模式调用
client
=
anthropic
.
Anthropic
(
api_key
=
"your-api-key"
,
base_url
=
"https://api.anthropic.com"
# 可选,代理服务需配置
)
response
=
client
.
messages
.
create
(
model
=
"claude-sonnet-4-6"
,
# 或 glm-4.7
max_tokens
=
8192
,
messages
=
[
...
]
)
```
#### CLI模式
通过本地claude命令行工具调用,使用现有Session Token:
-
安装:
`npm install -g @anthropic-ai/claude-code`
-
配置:
`AI_SERVICE_MODE=cli`
#### Auto模式(默认)
自动检测,优先使用API模式,若API模式不可用则使用CLI模式:
-
配置:
`AI_SERVICE_MODE=auto`
### 3.3 核心流程
```
┌─────────────────────────────────────────────────────────────┐
│ 主流程 │
├─────────────────────────────────────────────────────────────┤
│ 1. 读取配置 │
│ 2. 读取需求文档(.docx) │
│ 3. 读取开发PRD文档(.md) │
│ 4. 读取现有测试用例(.xlsx) │
│ 5. 提取功能点列表(AI分析) │
│ 6. 分析现有用例覆盖情况 │
│ 7. 检测缺失:功能点、场景、边界条件 │
│ 8. 生成新用例(AI生成) │
│ 9. 去重处理 │
│ 10. 生成完善后的测试用例文件 │
│ 11. 生成差异性报告 │
└─────────────────────────────────────────────────────────────┘
```
---
## 4. 详细设计
### 4.1 配置模块 (config.py)
```
python
# -*- coding: utf-8 -*-
"""
AI完善测试用例工具 - 配置模块
"""
from
pathlib
import
Path
# 项目路径
PROJECT_ROOT
=
Path
(
__file__
)
.
parent
.
parent
CONFIG_DIR
=
PROJECT_ROOT
/
"config"
PERFECTED_DIR
=
PROJECT_ROOT
/
"Perfected"
REPORTS_DIR
=
PROJECT_ROOT
/
"reports"
LOGS_DIR
=
PROJECT_ROOT
/
"logs"
# 输入路径
REQUIREMENTS_DIR
=
CONFIG_DIR
/
"需求文档"
PRD_DIR
=
CONFIG_DIR
/
"开发PRD"
TEST_CASE_FILE
=
CONFIG_DIR
/
"测试用例"
/
"新统一平台权限管理测试用例.xlsx"
# 输出路径
OUTPUT_TEST_CASE
=
PERFECTED_DIR
/
"新统一平台权限管理测试用例_完善版本.xlsx"
OUTPUT_REPORT
=
REPORTS_DIR
/
"新统一平台权限管理测试用例_差异性报告.docx"
# 测试用例列定义
TEST_CASE_COLUMNS
=
[
"序列号"
,
"功能模块"
,
"用例编号"
,
"功能描述"
,
"用例等级"
,
"功能编号"
,
"用例名称"
,
"预置条件"
,
"STEP"
,
"JSON"
,
"预期结果"
,
"测试结果"
,
"测试频次"
,
"日志/截图/照片"
,
"备注"
]
# AI配置
AI_MODEL
=
"claude-sonnet-4-6"
# 或 "claude-opus-4-6"、"glm-4.7"
AI_MAX_TOKENS
=
8192
AI_TEMPERATURE
=
0.3
# 较低温度确保稳定性
# API密钥环境变量名
API_KEY_ENV
=
"ANTHROPIC_API_KEY"
# API基础URL(可选,用于代理服务)
API_BASE_URL
=
"https://api.anthropic.com"
# AI服务模式:auto(自动检测)、api(Anthropic API)、cli(Claude CLI)
AI_SERVICE_MODE
=
"auto"
# 覆盖率目标
COVERAGE_TARGET
=
0.9
# 90%
# 日志配置
LOG_FORMAT
=
"
%(asctime)
s -
%(name)
s -
%(levelname)
s -
%(message)
s"
LOG_DATE_FORMAT
=
"
%
Y-
%
m-
%
d
%
H:
%
M:
%
S"
```
### 4.2 文档读取模块 (document_reader.py)
```
python
# -*- coding: utf-8 -*-
"""
文档读取模块
"""
from
pathlib
import
Path
from
typing
import
List
,
Dict
from
logging
import
getLogger
import
docx
import
openpyxl
logger
=
getLogger
(
__name__
)
class
DocumentReader
:
"""文档读取器"""
def
read_requirement_docs
(
self
,
requirements_dir
:
Path
)
->
List
[
Dict
]:
"""
读取需求文档(.docx)
Returns:
需求文档列表,每项包含文件名、内容、功能点列表
"""
docs
=
[]
for
docx_file
in
requirements_dir
.
glob
(
"*.docx"
):
logger
.
info
(
"读取需求文档:
%
s"
,
docx_file
.
name
)
doc
=
docx
.
Document
(
docx_file
)
content
=
"
\n
"
.
join
([
p
.
text
for
p
in
doc
.
paragraphs
if
p
.
text
.
strip
()])
docs
.
append
({
"filename"
:
docx_file
.
name
,
"path"
:
str
(
docx_file
),
"content"
:
content
})
return
docs
def
read_prd_docs
(
self
,
prd_dir
:
Path
)
->
List
[
Dict
]:
"""
读取开发PRD文档(.md)
Returns:
PRD文档列表,每项包含文件名、内容
"""
docs
=
[]
for
md_file
in
prd_dir
.
glob
(
"*.md"
):
logger
.
info
(
"读取PRD文档:
%
s"
,
md_file
.
name
)
with
open
(
md_file
,
"r"
,
encoding
=
"utf-8"
)
as
f
:
content
=
f
.
read
()
docs
.
append
({
"filename"
:
md_file
.
name
,
"path"
:
str
(
md_file
),
"content"
:
content
})
return
docs
def
read_test_cases
(
self
,
test_case_file
:
Path
)
->
List
[
Dict
]:
"""
读取测试用例(.xlsx)
Returns:
测试用例列表
"""
logger
.
info
(
"读取测试用例:
%
s"
,
test_case_file
.
name
)
wb
=
openpyxl
.
load_workbook
(
test_case_file
)
ws
=
wb
.
active
cases
=
[]
headers
=
[
cell
.
value
for
cell
in
ws
[
1
]]
for
row
in
ws
.
iter_rows
(
min_row
=
2
,
values_only
=
True
):
case
=
dict
(
zip
(
headers
,
row
))
if
case
.
get
(
"用例名称"
):
# 跳过空行
cases
.
append
(
case
)
logger
.
info
(
"读取到
%
d 条测试用例"
,
len
(
cases
))
return
cases
```
### 4.3 测试用例分析模块 (test_case_analyzer.py)
```
python
# -*- coding: utf-8 -*-
"""
测试用例分析模块
"""
from
typing
import
List
,
Dict
,
Set
from
logging
import
getLogger
import
anthropic
from
src.config
import
AI_MODEL
,
AI_MAX_TOKENS
,
AI_TEMPERATURE
logger
=
getLogger
(
__name__
)
class
TestCaseAnalyzer
:
"""测试用例分析器"""
def
__init__
(
self
,
api_key
:
str
):
self
.
client
=
anthropic
.
Anthropic
(
api_key
=
api_key
)
def
extract_function_points
(
self
,
requirements
:
List
[
Dict
],
prds
:
List
[
Dict
])
->
List
[
Dict
]:
"""
使用AI提取功能点列表
Args:
requirements: 需求文档列表
prds: PRD文档列表
Returns:
功能点列表,每项包含功能模块、功能编号、功能描述
"""
# 构建提示词
prompt
=
self
.
_build_extract_prompt
(
requirements
,
prds
)
response
=
self
.
client
.
messages
.
create
(
model
=
AI_MODEL
,
max_tokens
=
AI_MAX_TOKENS
,
temperature
=
AI_TEMPERATURE
,
messages
=
[
{
"role"
:
"user"
,
"content"
:
prompt
}
]
)
# 解析AI返回的功能点
result
=
response
.
content
[
0
]
.
text
function_points
=
self
.
_parse_function_points
(
result
)
logger
.
info
(
"提取到
%
d 个功能点"
,
len
(
function_points
))
return
function_points
def
_build_extract_prompt
(
self
,
requirements
:
List
[
Dict
],
prds
:
List
[
Dict
])
->
str
:
"""构建功能点提取提示词"""
prompt
=
"""请分析以下需求文档和PRD文档,提取完整的功能点列表。
【要求】
1. 按功能模块分类
2. 每个功能点包含:功能模块、功能编号、功能描述
3. 识别所有子功能
4. 输出格式为JSON数组
【需求文档】
"""
for
req
in
requirements
:
prompt
+=
f
"
\n
--- {req['filename']} ---
\n
{req['content'][:2000]}...
\n
"
prompt
+=
"
\n
【开发PRD文档】
\n
"
for
prd
in
prds
:
prompt
+=
f
"
\n
--- {prd['filename']} ---
\n
{prd['content'][:2000]}...
\n
"
prompt
+=
"""
\n
请输出JSON格式的功能点列表:
[
{
"功能模块": "模块名称",
"功能编号": "F-001",
"功能描述": "功能描述",
"子功能": ["子功能1", "子功能2"]
}
]
"""
return
prompt
def
_parse_function_points
(
self
,
result
:
str
)
->
List
[
Dict
]:
"""解析AI返回的功能点"""
import
json
try
:
# 提取JSON部分
start
=
result
.
find
(
"["
)
end
=
result
.
rfind
(
"]"
)
+
1
json_str
=
result
[
start
:
end
]
return
json
.
loads
(
json_str
)
except
Exception
as
e
:
logger
.
error
(
"解析功能点失败:
%
s"
,
e
)
return
[]
def
analyze_coverage
(
self
,
function_points
:
List
[
Dict
],
test_cases
:
List
[
Dict
])
->
Dict
:
"""
分析测试用例覆盖情况
Returns:
覆盖率分析结果
"""
covered_functions
=
set
()
for
case
in
test_cases
:
if
case
.
get
(
"功能描述"
):
covered_functions
.
add
(
case
[
"功能描述"
])
total_functions
=
len
(
function_points
)
covered_count
=
len
(
covered_functions
)
coverage_rate
=
covered_count
/
total_functions
if
total_functions
>
0
else
0
return
{
"total_functions"
:
total_functions
,
"covered_functions"
:
covered_count
,
"coverage_rate"
:
coverage_rate
,
"uncovered_functions"
:
total_functions
-
covered_count
}
```
### 4.4 缺失检测模块 (gap_detector.py)
```
python
# -*- coding: utf-8 -*-
"""
缺失检测模块
"""
from
typing
import
List
,
Dict
from
logging
import
getLogger
import
anthropic
from
src.config
import
AI_MODEL
,
AI_MAX_TOKENS
,
AI_TEMPERATURE
logger
=
getLogger
(
__name__
)
class
GapDetector
:
"""缺失检测器"""
def
__init__
(
self
,
api_key
:
str
):
self
.
client
=
anthropic
.
Anthropic
(
api_key
=
api_key
)
def
detect_gaps
(
self
,
function_points
:
List
[
Dict
],
test_cases
:
List
[
Dict
],
requirements
:
List
[
Dict
],
prds
:
List
[
Dict
])
->
Dict
:
"""
检测测试用例缺失情况
Returns:
缺失检测结果
"""
logger
.
info
(
"开始检测测试用例缺失..."
)
# 使用AI分析缺失情况
gaps
=
self
.
_ai_detect_gaps
(
function_points
,
test_cases
,
requirements
,
prds
)
return
{
"功能点缺失"
:
gaps
.
get
(
"功能点缺失"
,
[]),
"场景覆盖不足"
:
gaps
.
get
(
"场景覆盖不足"
,
[]),
"边界条件不足"
:
gaps
.
get
(
"边界条件不足"
,
[])
}
def
_ai_detect_gaps
(
self
,
function_points
:
List
[
Dict
],
test_cases
:
List
[
Dict
],
requirements
:
List
[
Dict
],
prds
:
List
[
Dict
])
->
Dict
:
"""使用AI检测缺失"""
prompt
=
self
.
_build_gap_prompt
(
function_points
,
test_cases
,
requirements
,
prds
)
response
=
self
.
client
.
messages
.
create
(
model
=
AI_MODEL
,
max_tokens
=
AI_MAX_TOKENS
,
temperature
=
AI_TEMPERATURE
,
messages
=
[{
"role"
:
"user"
,
"content"
:
prompt
}]
)
result
=
response
.
content
[
0
]
.
text
return
self
.
_parse_gap_result
(
result
)
def
_build_gap_prompt
(
self
,
function_points
:
List
[
Dict
],
test_cases
:
List
[
Dict
],
requirements
:
List
[
Dict
],
prds
:
List
[
Dict
])
->
str
:
"""构建缺失检测提示词"""
prompt
=
"""请分析以下功能点、需求文档、PRD文档和现有测试用例,识别测试用例的缺失情况。
【功能点列表】
"""
prompt
+=
str
(
function_points
)
prompt
+=
f
"
\n\n
【现有测试用例】(共{len(test_cases)}条)
\n
"
for
i
,
case
in
enumerate
(
test_cases
[:
20
]):
# 限制数量
prompt
+=
f
"{i+1}. {case.get('用例名称', '')} - {case.get('功能描述', '')}
\n
"
prompt
+=
"
\n
请识别以下三类缺失,输出JSON格式:"
prompt
+=
"""
{
"功能点缺失": [
{
"功能模块": "模块名",
"功能编号": "F-001",
"功能描述": "缺失的功能描述",
"建议用例名称": "测试XXX功能"
}
],
"场景覆盖不足": [
{
"功能描述": "XXX功能",
"缺失场景": "异常场景:XXX",
"建议用例名称": "测试XXX异常场景"
}
],
"边界条件不足": [
{
"功能描述": "XXX功能",
"缺失边界": "边界值:XXX",
"建议用例名称": "测试XXX边界条件"
}
]
}
"""
return
prompt
def
_parse_gap_result
(
self
,
result
:
str
)
->
Dict
:
"""解析AI返回的缺失结果"""
import
json
try
:
start
=
result
.
find
(
"{"
)
end
=
result
.
rfind
(
"}"
)
+
1
json_str
=
result
[
start
:
end
]
return
json
.
loads
(
json_str
)
except
Exception
as
e
:
logger
.
error
(
"解析缺失结果失败:
%
s"
,
e
)
return
{
"功能点缺失"
:
[],
"场景覆盖不足"
:
[],
"边界条件不足"
:
[]}
```
### 4.5 用例生成模块 (case_generator.py)
```
python
# -*- coding: utf-8 -*-
"""
用例生成模块
"""
from
typing
import
List
,
Dict
from
logging
import
getLogger
import
anthropic
from
src.config
import
AI_MODEL
,
AI_MAX_TOKENS
,
AI_TEMPERATURE
,
TEST_CASE_COLUMNS
logger
=
getLogger
(
__name__
)
class
CaseGenerator
:
"""测试用例生成器"""
def
__init__
(
self
,
api_key
:
str
):
self
.
client
=
anthropic
.
Anthropic
(
api_key
=
api_key
)
def
generate_cases
(
self
,
gaps
:
Dict
,
requirements
:
List
[
Dict
],
prds
:
List
[
Dict
])
->
List
[
Dict
]:
"""
生成缺失的测试用例
Returns:
新生成的测试用例列表
"""
logger
.
info
(
"开始生成测试用例..."
)
new_cases
=
[]
# 为功能点缺失生成用例
for
gap
in
gaps
.
get
(
"功能点缺失"
,
[]):
case
=
self
.
_generate_case_for_function
(
gap
,
requirements
,
prds
)
if
case
:
new_cases
.
append
(
case
)
# 为场景覆盖不足生成用例
for
gap
in
gaps
.
get
(
"场景覆盖不足"
,
[]):
case
=
self
.
_generate_case_for_scenario
(
gap
,
requirements
,
prds
)
if
case
:
new_cases
.
append
(
case
)
# 为边界条件不足生成用例
for
gap
in
gaps
.
get
(
"边界条件不足"
,
[]):
case
=
self
.
_generate_case_for_boundary
(
gap
,
requirements
,
prds
)
if
case
:
new_cases
.
append
(
case
)
logger
.
info
(
"生成
%
d 条新测试用例"
,
len
(
new_cases
))
return
new_cases
def
_generate_case_for_function
(
self
,
gap
:
Dict
,
requirements
:
List
[
Dict
],
prds
:
List
[
Dict
])
->
Dict
:
"""为功能点缺失生成测试用例"""
prompt
=
f
"""请为以下缺失的功能点生成完整的测试用例。
【功能信息】
- 功能模块:{gap.get('功能模块')}
- 功能编号:{gap.get('功能编号')}
- 功能描述:{gap.get('功能描述')}
请生成测试用例,输出JSON格式(包含以下字段):
{json.dumps({col: "" for col in TEST_CASE_COLUMNS}, ensure_ascii=False)}
要求:
1. 用例编号格式:TC-模块-序号
2. 用例等级:P1/P2/P3
3. STEP要详细,包含操作步骤
4. 预期结果要明确
"""
response
=
self
.
client
.
messages
.
create
(
model
=
AI_MODEL
,
max_tokens
=
AI_MAX_TOKENS
,
temperature
=
AI_TEMPERATURE
,
messages
=
[{
"role"
:
"user"
,
"content"
:
prompt
}]
)
result
=
response
.
content
[
0
]
.
text
return
self
.
_parse_case
(
result
)
def
_generate_case_for_scenario
(
self
,
gap
:
Dict
,
requirements
:
List
[
Dict
],
prds
:
List
[
Dict
])
->
Dict
:
"""为场景覆盖不足生成测试用例"""
# 类似实现
pass
def
_generate_case_for_boundary
(
self
,
gap
:
Dict
,
requirements
:
List
[
Dict
],
prds
:
List
[
Dict
])
->
Dict
:
"""为边界条件不足生成测试用例"""
# 类似实现
pass
def
_parse_case
(
self
,
result
:
str
)
->
Dict
:
"""解析AI返回的测试用例"""
import
json
try
:
start
=
result
.
find
(
"{"
)
end
=
result
.
rfind
(
"}"
)
+
1
json_str
=
result
[
start
:
end
]
return
json
.
loads
(
json_str
)
except
Exception
as
e
:
logger
.
error
(
"解析测试用例失败:
%
s"
,
e
)
return
{}
```
### 4.6 去重模块 (deduplicator.py)
```
python
# -*- coding: utf-8 -*-
"""
去重模块
"""
from
typing
import
List
,
Dict
from
logging
import
getLogger
logger
=
getLogger
(
__name__
)
class
Deduplicator
:
"""测试用例去重器"""
def
deduplicate
(
self
,
existing_cases
:
List
[
Dict
],
new_cases
:
List
[
Dict
])
->
Dict
:
"""
去重处理
Returns:
去重结果,包含唯一用例列表、重复用例列表
"""
logger
.
info
(
"开始去重处理..."
)
# 构建现有用例的指纹集合
existing_fingerprints
=
set
()
for
case
in
existing_cases
:
fp
=
self
.
_generate_fingerprint
(
case
)
existing_fingerprints
.
add
(
fp
)
# 筛选新用例
unique_cases
=
[]
duplicate_cases
=
[]
for
case
in
new_cases
:
fp
=
self
.
_generate_fingerprint
(
case
)
if
fp
in
existing_fingerprints
:
duplicate_cases
.
append
(
case
)
else
:
unique_cases
.
append
(
case
)
existing_fingerprints
.
add
(
fp
)
logger
.
info
(
"去重完成:唯一
%
d 条,重复
%
d 条"
,
len
(
unique_cases
),
len
(
duplicate_cases
))
return
{
"unique_cases"
:
unique_cases
,
"duplicate_cases"
:
duplicate_cases
,
"duplicate_count"
:
len
(
duplicate_cases
)
}
def
_generate_fingerprint
(
self
,
case
:
Dict
)
->
str
:
"""生成用例指纹(用于去重)"""
# 使用功能描述+用例名称+预期结果生成唯一标识
key_parts
=
[
case
.
get
(
"功能描述"
,
""
),
case
.
get
(
"用例名称"
,
""
),
case
.
get
(
"预期结果"
,
""
)
]
return
"|"
.
join
(
key_parts
)
.
strip
()
.
lower
()
```
### 4.7 报告生成模块 (report_generator.py)
```
python
# -*- coding: utf-8 -*-
"""
报告生成模块
"""
from
pathlib
import
Path
from
typing
import
List
,
Dict
from
logging
import
getLogger
from
datetime
import
datetime
from
docx
import
Document
from
docx.shared
import
Pt
,
RGBColor
logger
=
getLogger
(
__name__
)
class
ReportGenerator
:
"""差异性报告生成器"""
def
generate_report
(
self
,
gaps
:
Dict
,
dedup_result
:
Dict
,
coverage
:
Dict
,
output_path
:
Path
)
->
None
:
"""
生成差异性报告
"""
logger
.
info
(
"开始生成差异性报告..."
)
doc
=
Document
()
# 标题
title
=
doc
.
add_heading
(
"测试用例完善差异性报告"
,
0
)
title
.
alignment
=
1
# 居中
# 生成时间
doc
.
add_paragraph
(
f
"生成时间:{datetime.now().strftime('
%
Y-
%
m-
%
d
%
H:
%
M:
%
S')}"
)
# 1. 功能点缺失
self
.
_add_missing_functions_section
(
doc
,
gaps
.
get
(
"功能点缺失"
,
[]))
# 2. 场景覆盖不足
self
.
_add_scenarios_section
(
doc
,
gaps
.
get
(
"场景覆盖不足"
,
[]))
# 3. 边界条件不足
self
.
_add_boundaries_section
(
doc
,
gaps
.
get
(
"边界条件不足"
,
[]))
# 4. 去重处理结果
self
.
_add_deduplication_section
(
doc
,
dedup_result
)
# 5. 统计信息
self
.
_add_statistics_section
(
doc
,
coverage
,
dedup_result
)
# 保存
output_path
.
parent
.
mkdir
(
parents
=
True
,
exist_ok
=
True
)
doc
.
save
(
str
(
output_path
))
logger
.
info
(
"报告生成完成:
%
s"
,
output_path
)
def
_add_missing_functions_section
(
self
,
doc
:
Document
,
gaps
:
List
[
Dict
])
->
None
:
"""添加功能点缺失部分"""
doc
.
add_heading
(
"一、功能点缺失的测试用例"
,
level
=
1
)
if
not
gaps
:
doc
.
add_paragraph
(
"无功能点缺失"
)
else
:
for
i
,
gap
in
enumerate
(
gaps
,
1
):
p
=
doc
.
add_paragraph
()
p
.
add_run
(
f
"{i}. "
)
.
bold
=
True
p
.
add_run
(
f
"{gap.get('功能描述', '')}({gap.get('功能模块', '')})"
)
def
_add_scenarios_section
(
self
,
doc
:
Document
,
gaps
:
List
[
Dict
])
->
None
:
"""添加场景覆盖不足部分"""
doc
.
add_heading
(
"二、场景覆盖不足的测试用例"
,
level
=
1
)
if
not
gaps
:
doc
.
add_paragraph
(
"无场景覆盖不足"
)
else
:
for
i
,
gap
in
enumerate
(
gaps
,
1
):
p
=
doc
.
add_paragraph
()
p
.
add_run
(
f
"{i}. "
)
.
bold
=
True
p
.
add_run
(
f
"{gap.get('功能描述', '')} - {gap.get('缺失场景', '')}"
)
def
_add_boundaries_section
(
self
,
doc
:
Document
,
gaps
:
List
[
Dict
])
->
None
:
"""添加边界条件不足部分"""
doc
.
add_heading
(
"三、边界条件不足的测试用例"
,
level
=
1
)
if
not
gaps
:
doc
.
add_paragraph
(
"无边界条件不足"
)
else
:
for
i
,
gap
in
enumerate
(
gaps
,
1
):
p
=
doc
.
add_paragraph
()
p
.
add_run
(
f
"{i}. "
)
.
bold
=
True
p
.
add_run
(
f
"{gap.get('功能描述', '')} - {gap.get('缺失边界', '')}"
)
def
_add_deduplication_section
(
self
,
doc
:
Document
,
dedup_result
:
Dict
)
->
None
:
"""添加去重处理结果部分"""
doc
.
add_heading
(
"四、去重处理结果"
,
level
=
1
)
doc
.
add_paragraph
(
f
"新增用例中,去重后保留 {dedup_result.get('duplicate_count', 0)} 条唯一用例。"
)
def
_add_statistics_section
(
self
,
doc
:
Document
,
coverage
:
Dict
,
dedup_result
:
Dict
)
->
None
:
"""添加统计信息部分"""
doc
.
add_heading
(
"五、用例数量增长统计"
,
level
=
1
)
total_before
=
coverage
.
get
(
"covered_functions"
,
0
)
total_after
=
total_before
+
dedup_result
.
get
(
"duplicate_count"
,
0
)
# 表格
table
=
doc
.
add_table
(
rows
=
4
,
cols
=
2
)
table
.
style
=
'Light Grid Accent 1'
table
.
rows
[
0
]
.
cells
[
0
]
.
text
=
"统计项"
table
.
rows
[
0
]
.
cells
[
1
]
.
text
=
"数量"
table
.
rows
[
1
]
.
cells
[
0
]
.
text
=
"原有测试用例数"
table
.
rows
[
1
]
.
cells
[
1
]
.
text
=
str
(
total_before
)
table
.
rows
[
2
]
.
cells
[
0
]
.
text
=
"新增测试用例数(去重后)"
table
.
rows
[
2
]
.
cells
[
1
]
.
text
=
str
(
dedup_result
.
get
(
"duplicate_count"
,
0
))
table
.
rows
[
3
]
.
cells
[
0
]
.
text
=
"完善后测试用例总数"
table
.
rows
[
3
]
.
cells
[
1
]
.
text
=
str
(
total_after
)
# 覆盖率
doc
.
add_heading
(
"六、覆盖率百分比统计"
,
level
=
1
)
coverage_rate
=
coverage
.
get
(
"coverage_rate"
,
0
)
*
100
doc
.
add_paragraph
(
f
"当前覆盖率:{coverage_rate:.1f}
%
"
)
doc
.
add_paragraph
(
f
"目标覆盖率:90
%
"
)
if
coverage_rate
>=
90
:
doc
.
add_paragraph
(
"✅ 覆盖率达标!"
)
else
:
doc
.
add_paragraph
(
f
"⚠️ 距离目标还差 {90 - coverage_rate:.1f}
%
"
)
```
### 4.8 主入口模块 (main.py)
```
python
# -*- coding: utf-8 -*-
"""
AI完善测试用例工具 - 主入口模块
"""
import
os
import
sys
import
logging
from
pathlib
import
Path
from
src.config
import
*
from
src.document_reader
import
DocumentReader
from
src.test_case_analyzer
import
TestCaseAnalyzer
from
src.gap_detector
import
GapDetector
from
src.case_generator
import
CaseGenerator
from
src.deduplicator
import
Deduplicator
from
src.report_generator
import
ReportGenerator
# 配置日志
logging
.
basicConfig
(
level
=
logging
.
INFO
,
format
=
LOG_FORMAT
,
datefmt
=
LOG_DATE_FORMAT
,
handlers
=
[
logging
.
StreamHandler
(
sys
.
stdout
),
logging
.
FileHandler
(
LOGS_DIR
/
"ai_test_case_enhancer.log"
,
encoding
=
"utf-8"
)
]
)
logger
=
logging
.
getLogger
(
__name__
)
def
main
():
"""主函数"""
logger
.
info
(
"="
*
60
)
logger
.
info
(
"AI完善测试用例工具 启动"
)
logger
.
info
(
"="
*
60
)
# 检查API密钥
api_key
=
os
.
environ
.
get
(
"ANTHROPIC_API_KEY"
)
if
not
api_key
:
logger
.
error
(
"未设置ANTHROPIC_API_KEY环境变量"
)
logger
.
error
(
"请设置:export ANTHROPIC_API_KEY=your-key-here"
)
return
1
# 确保目录存在
PERFECTED_DIR
.
mkdir
(
parents
=
True
,
exist_ok
=
True
)
REPORTS_DIR
.
mkdir
(
parents
=
True
,
exist_ok
=
True
)
LOGS_DIR
.
mkdir
(
parents
=
True
,
exist_ok
=
True
)
try
:
# 1. 读取文档
reader
=
DocumentReader
()
requirements
=
reader
.
read_requirement_docs
(
REQUIREMENTS_DIR
)
prds
=
reader
.
read_prd_docs
(
PRD_DIR
)
test_cases
=
reader
.
read_test_cases
(
TEST_CASE_FILE
)
logger
.
info
(
"读取到需求文档
%
d 份"
,
len
(
requirements
))
logger
.
info
(
"读取到PRD文档
%
d 份"
,
len
(
prds
))
logger
.
info
(
"读取到测试用例
%
d 条"
,
len
(
test_cases
))
# 2. 提取功能点
analyzer
=
TestCaseAnalyzer
(
api_key
)
function_points
=
analyzer
.
extract_function_points
(
requirements
,
prds
)
# 3. 分析覆盖率
coverage
=
analyzer
.
analyze_coverage
(
function_points
,
test_cases
)
logger
.
info
(
"当前覆盖率:
%.1
f
%%
"
,
coverage
[
"coverage_rate"
]
*
100
)
# 4. 检测缺失
detector
=
GapDetector
(
api_key
)
gaps
=
detector
.
detect_gaps
(
function_points
,
test_cases
,
requirements
,
prds
)
logger
.
info
(
"功能点缺失:
%
d 项"
,
len
(
gaps
.
get
(
"功能点缺失"
,
[])))
logger
.
info
(
"场景覆盖不足:
%
d 项"
,
len
(
gaps
.
get
(
"场景覆盖不足"
,
[])))
logger
.
info
(
"边界条件不足:
%
d 项"
,
len
(
gaps
.
get
(
"边界条件不足"
,
[])))
# 5. 生成新用例
generator
=
CaseGenerator
(
api_key
)
new_cases
=
generator
.
generate_cases
(
gaps
,
requirements
,
prds
)
# 6. 去重
deduplicator
=
Deduplicator
()
dedup_result
=
deduplicator
.
deduplicate
(
test_cases
,
new_cases
)
# 7. 合并用例并保存
import
openpyxl
wb
=
openpyxl
.
load_workbook
(
TEST_CASE_FILE
)
ws
=
wb
.
active
# 追加新用例
for
case
in
dedup_result
[
"unique_cases"
]:
ws
.
append
([
case
.
get
(
col
,
""
)
for
col
in
TEST_CASE_COLUMNS
])
wb
.
save
(
str
(
OUTPUT_TEST_CASE
))
logger
.
info
(
"完善后的测试用例已保存:
%
s"
,
OUTPUT_TEST_CASE
)
# 8. 生成报告
reporter
=
ReportGenerator
()
reporter
.
generate_report
(
gaps
,
dedup_result
,
coverage
,
OUTPUT_REPORT
)
logger
.
info
(
"="
*
60
)
logger
.
info
(
"处理完成!"
)
logger
.
info
(
"完善后的测试用例:
%
s"
,
OUTPUT_TEST_CASE
)
logger
.
info
(
"差异性报告:
%
s"
,
OUTPUT_REPORT
)
logger
.
info
(
"="
*
60
)
return
0
except
Exception
as
e
:
logger
.
error
(
"处理失败:
%
s"
,
e
)
import
traceback
logger
.
debug
(
traceback
.
format_exc
())
return
1
if
__name__
==
"__main__"
:
sys
.
exit
(
main
())
```
---
## 5. 实施计划
### 5.1 实施步骤
| 序号 | 任务 | 预计时间 | 状态 |
|------|------|----------|------|
| 1 | 创建项目目录结构 | 0.5h | ⏳ 待开始 |
| 2 | 实现配置模块 (config.py) | 0.5h | ⏳ 待开始 |
| 3 | 实现文档读取模块 (document_reader.py) | 2h | ⏳ 待开始 |
| 4 | 实现测试用例分析模块 (test_case_analyzer.py) | 3h | ⏳ 待开始 |
| 5 | 实现缺失检测模块 (gap_detector.py) | 3h | ⏳ 待开始 |
| 6 | 实现用例生成模块 (case_generator.py) | 4h | ⏳ 待开始 |
| 7 | 实现去重模块 (deduplicator.py) | 1h | ⏳ 待开始 |
| 8 | 实现报告生成模块 (report_generator.py) | 2h | ⏳ 待开始 |
| 9 | 实现主入口模块 (main.py) | 1h | ⏳ 待开始 |
| 10 | 创建入口脚本 (run.py) | 0.5h | ⏳ 待开始 |
| 11 | 编写requirements.txt | 0.5h | ⏳ 待开始 |
| 12 | 编写README.md | 1h | ⏳ 待开始 |
| 13 | 单元测试 | 2h | ⏳ 待开始 |
| 14 | 集成测试 | 2h | ⏳ 待开始 |
| 15 | 文档完善 | 1h | ⏳ 待开始 |
**总计**
:约 24 小时
### 5.2 依赖包
```
anthropic>=0.18.0
openpyxl>=3.1.0
python-docx>=1.1.0
pandas>=2.0.0
```
---
## 6. 测试验证
### 6.1 单元测试
-
测试文档读取功能
-
测试功能点提取功能
-
测试缺失检测功能
-
测试用例生成功能
-
测试去重功能
### 6.2 集成测试
-
端到端流程测试
-
使用实际文档测试
### 6.3 验收标准
| 验收项 | 标准 |
|--------|------|
| 功能点提取 | 能正确提取所有功能点 |
| 缺失检测 | 能识别功能点、场景、边界条件缺失 |
| 用例生成 | 生成符合格式的测试用例 |
| 去重 | 能正确识别并去除重复用例 |
| 报告生成 | 生成完整的差异性报告 |
| 覆盖率 | 最终覆盖率达到90% |
---
## 7. 注意事项
1.
**API密钥安全**
:ANTHROPIC_API_KEY 应通过环境变量设置,不要硬编码在代码中
2.
**提示词优化**
:实际使用中可能需要根据AI返回结果优化提示词
3.
**成本控制**
:大量API调用可能产生费用,建议先小规模测试
4.
**人工审核**
:AI生成的测试用例需要人工审核确认
---
## 8. 后续优化
-
[
]
支持自定义AI提示词模板
-
[
]
支持增量更新(只处理变更的部分)
-
[
]
支持多轮对话优化用例
-
[
]
支持导出为多种格式
-
[
]
添加用例质量评分
-
[
]
支持批量处理多个项目
Docs/PRD/用例生成/_PRD_根据PRD文档生成测试用例.md
deleted
100644 → 0
浏览文件 @
e50d943e
# _PRD_根据PRD文档生成测试用例.md
> 版本:V1.1
> 更新日期:2026-01-28
> 适用范围:自动化测试用例生成工具
> 实现脚本:`AuxiliaryTool\用例生成\create_test_xlsx.py`
---
## 1. 背景与目标
### 1.1 背景
测试用例编写是测试工作的基础,但手工从 PRD 文档中提取需求并编写测试用例耗时耗力。现有项目 PRD 文档采用 Markdown 格式编写,具备结构化的编号和描述,适合自动化解析。
### 1.2 目标
实现一套自动化测试用例生成工具,具备:
-
自动从 PRD(Markdown)文档中抽取需求条目
-
基于规则引擎生成测试用例记录(JSON + Excel)
-
支持交互式选择多个 PRD 文档
-
生成的用例符合现有测试用例模板格式
---
## 2. 总体范围
### 2.1 纳入范围
1.
**PRD 文档解析**
-
支持 Markdown 格式的 PRD 文档
-
自动识别编号结构(如
`1.1、2.3.1`
)
-
兼容 Markdown 标题语法(
`# 标题`
)
2.
**测试用例生成**
-
自动生成测试用例 JSON 文件
-
写入 Excel 测试用例模板
3.
**交互式操作**
-
列出可选 PRD 文档
-
支持多选(如
`1,2,5`
或
`1 2 5`
)
-
自定义 Sheet 名称和输出文件名
### 2.2 不在本期范围
-
PRD 文档的语法校验
-
测试用例的智能去重
-
复杂的测试数据生成
-
用例间的依赖关系处理
---
## 3. 术语说明
-
**需求条目**
:PRD 文档中带有编号的功能描述项
-
**用例编号前缀**
:测试用例 ID 的前缀(默认
`TC`
)
-
**功能模块**
:测试用例所属的模块名称
-
**Sheet 复用**
:多 PRD 选中时,用例写入同一个 Sheet(追加模式)
---
## 4. 功能需求
### 4.1 PRD 目录定位
#### 4.1.1 目录兼容
脚本需兼容以下两种 PRD 目录结构:
-
`Docs/开发PRD`
(PRD 文档中约定的路径)
-
`Docs/PRD`
(仓库中实际常见的路径)
#### 4.1.2 自动检测逻辑
-
按优先级检测目录是否存在
-
若均不存在,使用第一个路径并在后续报错提示
#### 4.1.3 相关路径
| 配置项 | 路径 |
| --- | --- |
| PRD 目录 |
`Docs/开发PRD`
或
`Docs/PRD`
|
| 模板文件 |
`AuxiliaryTool/用例生成/用例文件/兰州中石化项目测试用例20251203.xlsx`
|
| JSON 输出 |
`AuxiliaryTool/用例生成/config/`
|
| Excel 输出 |
`AuxiliaryTool/用例生成/用例文件/`
|
---
### 4.2 需求条目抽取规则
#### 4.2.1 编号识别
支持以下两种格式:
1.
**点分编号**
:正则
`^\s*(\d+(?:\.\d+)*)\s*[、\.\-]\s*(.+?)\s*[::]?\s*$`
-
示例:
`1.1 用户登录`
、
`2.3.1-异常处理`
2.
**Markdown 标题**
:正则
`^\s*(#{1,6})\s+(.+?)\s*$`
-
示例:
`## 用户登录`
、
`### 异常处理`
#### 4.2.2 内容抽取
-
标题后续行作为详情内容(直到下一个标题)
-
**Markdown 标题嵌套处理**
:如果当前已有标题,遇到新的 Markdown 标题时,该标题行会被作为详情内容追加,而非创建新条目
-
自动过滤:
-
代码块(
```...```
)
-
HTML 注释(
<!--...-->
)
-
无效标题(说明、目录、背景、概述)
-
标题过滤条件:标题长度必须 ≥ 2 个字符
#### 4.2.3 清洗规则
-
移除列表符号(
`-`
、
`*`
、
`+`
)
-
合并多余空格
-
保留文本核心语义
---
### 4.3 测试用例分类与优先级
#### 4.3.1 功能类别判定
| 类别 | 关键词 |
| --- | --- |
| 异常场景 | 异常、失败、错误、告警、报警、超时、断开、暴涨 |
| 安全/鉴权 | 安全、权限、鉴权、加密、脱敏 |
| 运维可观测 | 报告、输出、word、markdown、邮件、钉钉、通知 |
| 功能测试 | 其他(默认) |
#### 4.3.2 用例等级判定
| 等级 | 关键词 |
| --- | --- |
| 高 | 必须、报警、告警、峰值、暴涨、发送、对接 |
| 中 | 待实现、可以、建议、优化 |
| 中 | 其他(默认) |
#### 4.3.3 用例字段生成规则
| 字段 | 生成规则 |
| --- | --- |
| 序号 | 递增(多 PRD 时全局递增,跨 PRD 累加) |
| 用例编号 |
`{前缀}-{序号:03d}`
,如
`TC-001`
、
`TC-002`
|
| 用例名称 | 需求标题 |
| 功能描述 | 需求标题 |
| 功能编号 | 抽取的编号(可能为空,适用于 Markdown 标题) |
| 预置条件 | 固定:
`1. 环境已部署; 2. 具备执行权限; 3. 必要依赖/配置已准备`
|
| 操作步骤 | 从详情中提取含"检查、监测、记录、输出、发送、查询、进入、生成、判定、调用、执行"的句子;最多提取 6 条,去重后以分号分隔;
**在 Excel 中分号会转换为换行显示**
;若无匹配内容,使用默认值:
`1. 按 PRD 描述执行该功能/流程; 2. 采集相关日志/输出; 3. 记录实际结果`
|
| 预期结果 | 从详情中提取含"需要、应、必须、输出、记录、发送、判定、成功、失败"的句子;最多提取 6 条,去重后以分号分隔;若无匹配内容,使用默认值:
`{标题} 按 PRD 约定产出正确结果(请补充具体断言点)`
|
| JSON |
**Excel 中强制清空(仅 JSON 文件保留)**
|
| 测试结果 | 空(待人工填写) |
| 测试结论 | 空(待人工填写) |
| 日志截屏 | 空(待人工填写) |
| 备注 | 空(待人工填写) |
---
### 4.4 交互式选择
#### 4.4.1 PRD 列表展示
-
列出 PRD 目录下所有
`.md`
文件
-
按文件名排序显示
-
格式:
`序号. 文件名`
#### 4.4.2 多选输入
-
支持格式:
`1,2,5`
或
`1 2 5`
-
支持逗号或空格分隔
-
自动去重
-
输入验证:序号必须在范围内
#### 4.4.3 自定义名称
1.
**Sheet 名称**
-
单 PRD 默认:
`{PRD文件名}_用例`
-
多 PRD 默认:
`多PRD_用例`
-
用户可覆盖输入
-
**名称安全处理**
:
-
自动过滤 Excel 不允许的字符:
`[ ] : * ? / \` 替换为 `
_
`
- 限制最大长度:**31 字符**(Excel Sheet 名称限制)
2. **输出文件名**
- 单 PRD 默认:`
{PRD文件名}
`
- 多 PRD 默认:`
MultiPRD
`
- 自动添加时间戳:`
{名称}_{YYYYMMDD_HHMMSS}.xlsx
`
---
### 4.5 Excel 输出
#### 4.5.1 表头顺序
序号、功能模块、功能类别、用例编号、功能描述、用例等级、功能编号、用例名称、预置条件、操作步骤、JSON、预期结果、测试结果、测试结论、日志截屏、备注
#### 4.5.2 Sheet 处理
- **表头查找**:在模板的前 30 行中,查找包含至少 2 个关键字(`
序号
`、`
用例名称
`、`
操作步骤
`、`
预期结果
`)的行作为表头行;若未找到,默认使用第 3 行
- 若 Sheet 不存在:创建新 Sheet,复制模板表头行和样式(字体、填充、边框、对齐、数字格式)
- 若 Sheet 已存在:追加数据(多 PRD 场景),从当前最大行下一行开始写入
- 冻结首行:`
B2
`
#### 4.5.3 样式设置
- **自动换行**:预置条件、操作步骤列
- **对齐方式**:顶部对齐
- **列宽**:根据内容自适应,最大 60
#### 4.5.4 JSON 输出
- 每个 PRD 单独生成 JSON 文件
- 文件名:`
{PRD文件名}_用例.json
`
- 存放目录:`
AuxiliaryTool/用例生成/config/
`
---
## 5. 配置与可变项
### 5.1 命令行参数
脚本支持通过命令行参数覆盖默认配置:
| 参数 | 默认值 | 说明 |
| --- | --- | --- |
| `
--template
` | `
兰州中石化项目测试用例20251203.xlsx
` | 模板 Excel 文件路径 |
| `
--module
` | `
通用模块
` | 功能模块字段值(多 PRD 时统一使用该值) |
| `
--prefix
` | `
TC
` | 用例编号前缀(多 PRD 时统一使用该值) |
| `
--overwrite
` | `
false
` | 是否覆盖保存到模板文件(默认另存为新文件) |
### 5.2 可变配置项
| 配置项 | 默认值 | 说明 |
| --- | --- | --- |
| PRD 目录 | `
Docs/开发PRD
` 或 `
Docs/PRD
` | 自动检测,优先使用存在的目录 |
| 模板文件 | `
兰州中石化项目测试用例20251203.xlsx
` | 位于 `
AuxiliaryTool/用例生成/用例文件/
` |
| JSON 输出目录 | `
config/
` | 位于 `
AuxiliaryTool/用例生成/
` |
| Excel 输出目录 | `
用例文件/
` | 位于 `
AuxiliaryTool/用例生成/
` |
---
## 6. 异常处理与容错要求
1. **PRD 目录为空或不存在**:明确报错提示路径
2. **模板文件不存在**:提示文件路径并退出
3. **无效输入**:提示重试,不退出
4. **无可用需求条目**:记录日志,跳过该 PRD
5. **写入失败**:明确提示错误原因
---
## 7. 交付物
1. Python 脚本:
- `
AuxiliaryTool/用例生成/create_test_xlsx.py
`
2. 配置输出:
- `
AuxiliaryTool/用例生成/config/{PRD名}_用例.json
`
3. 用例输出:
- `
AuxiliaryTool/用例生成/用例文件/{名称}_{时间戳}.xlsx
`
---
## 8. 验收标准
1. 能正确解析 PRD 文档中的编号结构
2. 生成的用例分类和优先级符合规则
3. Excel 输出格式与模板一致
4. 支持多 PRD 合并到同一 Sheet
5. 所有操作有明确日志输出
6. 异常情况有友好提示
---
## 9. 使用示例
### 9.1 基本使用(交互式)
```bash
# 执行脚本
python AuxiliaryTool/用例生成/create_test_xlsx.py
# 交互流程
PRD目录:C:\PycharmData\ubains-module-test\Docs\PRD
1. _PRD_预定系统后端服务监测需求文档.md
2. _PRD_其他需求文档.md
支持多选:输入多个序号(如 1,2,5 或 1 2 5)。
请输入PRD序号(可多选):1
请输入Sheet名称(回车使用默认:_PRD_预定系统后端服务监测需求文档_用例):
请输入生成的测试用例文件名称(回车使用默认:_PRD_预定系统后端服务监测需求文档):
# 输出
已生成 JSON:AuxiliaryTool/用例生成/config/_PRD_预定系统后端服务监测需求文档_用例.json(15条)
已生成Excel:AuxiliaryTool/用例生成/用例文件/_PRD_预定系统后端服务监测需求文档_20260128_150000.xlsx
Sheet:_PRD_预定系统后端服务监测需求文档_用例
总用例条数:15
```
### 9.2 使用命令行参数
```bash
# 指定功能模块和用例前缀
python AuxiliaryTool/用例生成/create_test_xlsx.py --module "会议系统" --prefix "MT"
# 使用自定义模板文件
python AuxiliaryTool/用例生成/create_test_xlsx.py --template "自定义模板.xlsx"
# 覆盖模式(直接覆盖模板文件,不另存)
python AuxiliaryTool/用例生成/create_test_xlsx.py --overwrite
# 组合使用
python AuxiliaryTool/用例生成/create_test_xlsx.py --module "会议系统" --prefix "MT" --template "自定义模板.xlsx"
```
### 9.3 多 PRD 合并示例
```bash
# 选择多个 PRD
请输入PRD序号(可多选):1 2 3
请输入Sheet名称(回车使用默认:多PRD_用例):
请输入生成的测试用例文件名称(回车使用默认:MultiPRD):
# 输出:三个 PRD 的用例合并在同一个 Sheet 中,序号全局递增
已生成 JSON:AuxiliaryTool/用例生成/config/_PRD_预定系统后端服务监测需求文档_用例.json(15条)
已生成 JSON:AuxiliaryTool/用例生成/config/_PRD_其他需求文档_用例.json(8条)
已生成 JSON:AuxiliaryTool/用例生成/config/_PRD_第三个文档_用例.json(12条)
已生成Excel:AuxiliaryTool/用例生成/用例文件/MultiPRD_20260128_150000.xlsx
Sheet:多PRD_用例
总用例条数:35
``
`
---
## 需求规范
代码规范:Docs/PRD/01规范文档/_PRD_规范文档_代码规范.md
问题总结:Docs/PRD/01规范文档/_PRD_问题总结_记录文档.md
方法总结:Docs/PRD/01规范文档/_PRD_方法总结_记录文档.md
文档规范:Docs/PRD/01规范文档/_PRD_规范文档_文档规范.md
测试规范:Docs/PRD/01规范文档/_PRD_规范文档_测试规范.md
Docs/PRD/用例生成/_PRD_根据PRD文档生成测试用例_计划执行.md
deleted
100644 → 0
浏览文件 @
e50d943e
# _PRD_根据PRD文档生成测试用例_计划执行.md
> 版本:V1.0
> 创建日期:2026-01-28
> 关联需求:`_PRD_根据PRD文档生成测试用例.md`
> 目标:基于现有脚本实现,输出完整的开发计划与技术规范
---
## 1. 开发概述
### 1.1 脚本基本信息
| 项目 | 内容 |
|------|------|
| 脚本路径 |
`AuxiliaryTool/用例生成/create_test_xlsx.py`
|
| 编程语言 | Python 3.x |
| 核心依赖 |
`openpyxl`
(Excel 操作) |
| 入口函数 |
`main()`
|
### 1.2 功能模块划分
```
create_test_xlsx.py
├── 1) 路径与常量定义
├── 2) PRD 解析与用例生成
│ ├── RequirementItem 数据类
│ ├── normalize_md() - Markdown 预处理
│ ├── extract_requirement_items() - 需求条目抽取
│ ├── classify_category() - 功能类别判定
│ ├── decide_priority() - 用例等级判定
│ ├── build_steps() - 操作步骤生成
│ ├── build_expected() - 预期结果生成
│ └── prd_to_cases() - PRD 转用例
├── 3) Excel 写入
│ ├── find_header_row() - 表头查找
│ ├── safe_sheet_name() - Sheet 名称安全处理
│ └── write_cases_to_sheet() - 写入 Sheet
└── 4) 交互式操作
├── pick_prds_interactively() - PRD 选择
├── ask_sheet_name_interactively() - Sheet 名称输入
└── ask_output_name_interactively() - 输出文件名输入
```
---
## 2. 开发任务清单
### 2.1 阶段一:基础框架(优先级:高)
#### 任务 1.1:路径与常量定义
**目标**
:定义所有路径常量和全局配置
**实现要点**
:
```
python
# 路径计算
BASE_DIR
=
os
.
path
.
dirname
(
os
.
path
.
abspath
(
__file__
))
REPO_ROOT
=
os
.
path
.
abspath
(
os
.
path
.
join
(
BASE_DIR
,
".."
,
".."
))
# PRD 目录候选(兼容两种路径)
PRD_DIR_CANDIDATES
=
[
os
.
path
.
join
(
REPO_ROOT
,
"Docs"
,
"开发PRD"
),
os
.
path
.
join
(
REPO_ROOT
,
"Docs"
,
"PRD"
),
]
def
resolve_prd_dir
()
->
str
:
"""按优先级检测 PRD 目录,返回存在的路径"""
for
d
in
PRD_DIR_CANDIDATES
:
if
os
.
path
.
isdir
(
d
):
return
d
return
PRD_DIR_CANDIDATES
[
0
]
# 默认第一个
```
**表头顺序常量**
:
```
python
headers_order
=
[
"序号"
,
"功能模块"
,
"功能类别"
,
"用例编号"
,
"功能描述"
,
"用例等级"
,
"功能编号"
,
"用例名称"
,
"预置条件"
,
"操作步骤"
,
"JSON"
,
"预期结果"
,
"测试结果"
,
"测试结论"
,
"日志截屏"
,
"备注"
,
]
```
**验收标准**
:
-
路径计算正确,跨平台兼容
-
`resolve_prd_dir()`
能正确检测存在的目录
---
#### 任务 1.2:命令行参数解析
**目标**
:支持命令行参数覆盖默认配置
**实现要点**
:
```
python
import
argparse
parser
=
argparse
.
ArgumentParser
(
description
=
"根据PRD生成用例JSON,并写入测试用例Excel"
)
parser
.
add_argument
(
"--template"
,
default
=
TEMPLATE_PATH_DEFAULT
,
help
=
"模板Excel路径"
)
parser
.
add_argument
(
"--module"
,
default
=
"通用模块"
,
help
=
"功能模块字段值"
)
parser
.
add_argument
(
"--prefix"
,
default
=
"TC"
,
help
=
"用例编号前缀"
)
parser
.
add_argument
(
"--overwrite"
,
action
=
"store_true"
,
help
=
"是否覆盖保存"
)
args
=
parser
.
parse_args
()
```
**验收标准**
:
-
`--help`
能正确显示帮助信息
-
各参数能正确覆盖默认值
---
### 2.2 阶段二:PRD 解析(优先级:高)
#### 任务 2.1:数据类定义
**目标**
:定义需求条目数据结构
**实现要点**
:
```
python
from
dataclasses
import
dataclass
from
typing
import
List
@
dataclass
class
RequirementItem
:
code
:
str
# 编号(如 "1.1")
title
:
str
# 标题
detail_lines
:
List
[
str
]
# 详情内容行列表
```
---
#### 任务 2.2:正则表达式定义
**目标**
:定义编号识别的正则表达式
**实现要点**
:
```
python
# 点分编号:1.1、2.3.1 等
RE_SECTION
=
re
.
compile
(
r"^\s*(?P<code>\d+(?:\.\d+)*)\s*[、\.\-]\s*(?P<title>.+?)\s*[::]?\s*$"
)
# Markdown 标题:# ## ### 等
RE_MD_HEADING
=
re
.
compile
(
r"^\s*(#{1,6})\s+(?P<title>.+?)\s*$"
)
```
**测试用例**
:
| 输入 | 匹配结果 | code | title |
|------|----------|------|-------|
|
`1.1 用户登录`
| ✓ |
`1.1`
|
`用户登录`
|
|
`2.3.1-异常处理`
| ✓ |
`2.3.1`
|
`异常处理`
|
|
`## 功能描述`
| ✓ | - |
`功能描述`
|
|
`### 接口定义`
| ✓ | - |
`接口定义`
|
---
#### 任务 2.3:Markdown 预处理
**目标**
:过滤代码块和 HTML 注释
**实现要点**
:
```
python
def
normalize_md
(
text
:
str
)
->
str
:
# 统一换行符
text
=
text
.
replace
(
"
\r\n
"
,
"
\n
"
)
.
replace
(
"
\r
"
,
"
\n
"
)
# 移除代码块
text
=
re
.
sub
(
r"```.*?```"
,
""
,
text
,
flags
=
re
.
S
)
# 移除 HTML 注释
text
=
re
.
sub
(
r"<!--.*?-->"
,
""
,
text
,
flags
=
re
.
S
)
return
text
```
**验收标准**
:
-
代码块被正确移除
-
HTML 注释被正确移除
-
换行符统一为
`\n`
---
#### 任务 2.4:需求条目抽取
**目标**
:从 Markdown 文本中抽取需求条目
**核心逻辑**
:
```
python
def
extract_requirement_items
(
md_text
:
str
)
->
List
[
RequirementItem
]:
lines
=
md_text
.
split
(
"
\n
"
)
items
:
List
[
RequirementItem
]
=
[]
current_code
=
""
current_title
=
""
current_detail
:
List
[
str
]
=
[]
def
flush
():
nonlocal
current_code
,
current_title
,
current_detail
if
current_title
:
detail
=
[
clean_line
(
x
)
for
x
in
current_detail
if
clean_line
(
x
)]
items
.
append
(
RequirementItem
(
code
=
current_code
,
title
=
clean_line
(
current_title
),
detail_lines
=
detail
))
current_code
=
""
current_title
=
""
current_detail
=
[]
for
raw
in
lines
:
line
=
raw
.
rstrip
(
"
\n
"
)
# 匹配点分编号
m
=
RE_SECTION
.
match
(
line
)
if
m
:
flush
()
current_code
=
m
.
group
(
"code"
)
.
strip
()
current_title
=
m
.
group
(
"title"
)
.
strip
()
continue
# 匹配 Markdown 标题
mh
=
RE_MD_HEADING
.
match
(
line
)
if
mh
:
if
current_title
:
# 已有标题,嵌套标题作为详情
current_detail
.
append
(
line
)
else
:
# 新标题
flush
()
current_code
=
""
current_title
=
mh
.
group
(
"title"
)
.
strip
()
continue
# 详情行
if
current_title
:
current_detail
.
append
(
line
)
flush
()
# 过滤:标题长度 >= 2
items
=
[
it
for
it
in
items
if
it
.
title
and
len
(
it
.
title
)
>=
2
]
return
items
```
**关键点**
:
1.
遇到点分编号时,
`flush()`
当前条目,开始新条目
2.
遇到 Markdown 标题时:
-
如果已有标题 → 作为详情追加(嵌套处理)
-
如果无标题 →
`flush()`
并创建新条目
3.
最后必须
`flush()`
一次
4.
过滤标题长度 < 2 的条目
---
#### 任务 2.5:行清洗
**目标**
:清洗单行文本
**实现要点**
:
```
python
def
clean_line
(
line
:
str
)
->
str
:
line
=
line
.
strip
()
# 移除列表符号
line
=
re
.
sub
(
r"^[\-\*\+]\s+"
,
""
,
line
)
# 合并多余空格
line
=
re
.
sub
(
r"\s+"
,
" "
,
line
)
return
line
```
---
### 2.3 阶段三:用例生成规则(优先级:高)
#### 任务 3.1:功能类别判定
**目标**
:根据关键词判定功能类别
**实现要点**
:
```
python
def
classify_category
(
title
:
str
,
detail_lines
:
List
[
str
])
->
str
:
text
=
(
title
+
" "
+
" "
.
join
(
detail_lines
))
.
lower
()
if
any
(
k
in
text
for
k
in
[
"异常"
,
"失败"
,
"错误"
,
"告警"
,
"报警"
,
"超时"
,
"断开"
,
"暴涨"
]):
return
"异常场景"
if
any
(
k
in
text
for
k
in
[
"安全"
,
"权限"
,
"鉴权"
,
"加密"
,
"脱敏"
]):
return
"安全/鉴权"
if
any
(
k
in
text
for
k
in
[
"报告"
,
"输出"
,
"word"
,
"markdown"
,
"邮件"
,
"钉钉"
,
"通知"
]):
return
"运维可观测"
return
"功能测试"
```
**判定顺序**
:异常场景 → 安全/鉴权 → 运维可观测 → 功能测试(默认)
---
#### 任务 3.2:用例等级判定
**目标**
:根据关键词判定用例等级
**实现要点**
:
```
python
def
decide_priority
(
title
:
str
,
detail_lines
:
List
[
str
])
->
str
:
text
=
title
+
" "
+
" "
.
join
(
detail_lines
)
if
any
(
k
in
text
for
k
in
[
"必须"
,
"报警"
,
"告警"
,
"峰值"
,
"暴涨"
,
"发送"
,
"对接"
]):
return
"高"
if
any
(
k
in
text
for
k
in
[
"待实现"
,
"可以"
,
"建议"
,
"优化"
]):
return
"中"
return
"中"
# 默认
```
**判定顺序**
:高 → 中(待实现) → 中(默认)
---
#### 任务 3.3:操作步骤生成
**目标**
:从详情中提取操作步骤
**实现要点**
:
```
python
def
build_steps
(
detail_lines
:
List
[
str
])
->
str
:
keywords
=
[
"检查"
,
"监测"
,
"记录"
,
"输出"
,
"发送"
,
"查询"
,
"进入"
,
"生成"
,
"判定"
,
"调用"
,
"执行"
]
candidates
:
List
[
str
]
=
[]
for
ln
in
detail_lines
:
s
=
clean_line
(
ln
)
if
not
s
:
continue
if
any
(
k
in
s
for
k
in
keywords
):
candidates
.
append
(
s
)
# 去重
uniq
:
List
[
str
]
=
[]
for
c
in
candidates
:
if
c
not
in
uniq
:
uniq
.
append
(
c
)
if
not
uniq
:
return
"1. 按 PRD 描述执行该功能/流程; 2. 采集相关日志/输出; 3. 记录实际结果"
uniq
=
uniq
[:
6
]
# 最多 6 条
return
"; "
.
join
([
f
"{i+1}. {x}"
for
i
,
x
in
enumerate
(
uniq
)])
```
**关键点**
:
-
关键词列表必须完整
-
去重逻辑保证不重复
-
最多 6 条
-
默认值必须准确
---
#### 任务 3.4:预期结果生成
**目标**
:从详情中提取预期结果
**实现要点**
:
```
python
def
build_expected
(
title
:
str
,
detail_lines
:
List
[
str
])
->
str
:
keywords
=
[
"需要"
,
"应"
,
"必须"
,
"输出"
,
"记录"
,
"发送"
,
"判定"
,
"成功"
,
"失败"
]
candidates
:
List
[
str
]
=
[]
for
ln
in
detail_lines
:
s
=
clean_line
(
ln
)
if
not
s
:
continue
if
any
(
k
in
s
for
k
in
keywords
):
candidates
.
append
(
s
)
# 去重
uniq
:
List
[
str
]
=
[]
for
c
in
candidates
:
if
c
not
in
uniq
:
uniq
.
append
(
c
)
if
not
uniq
:
return
f
"{title} 按 PRD 约定产出正确结果(请补充具体断言点)"
uniq
=
uniq
[:
6
]
# 最多 6 条
return
"; "
.
join
(
uniq
)
```
---
#### 任务 3.5:用例编号生成
**目标**
:生成格式化的用例编号
**实现要点**
:
```
python
def
make_case_id
(
prefix
:
str
,
idx
:
int
)
->
str
:
return
f
"{prefix}-{idx:03d}"
# TC-001, TC-002, ...
```
---
#### 任务 3.6:用例记录生成
**目标**
:将需求条目转换为用例记录
**实现要点**
:
```
python
def
to_case_record
(
idx
:
int
,
module
:
str
,
prefix
:
str
,
req
:
RequirementItem
)
->
Dict
[
str
,
str
]:
record
:
Dict
[
str
,
str
]
=
{
"序号"
:
idx
,
"功能模块"
:
module
,
"功能类别"
:
classify_category
(
req
.
title
,
req
.
detail_lines
),
"用例编号"
:
make_case_id
(
prefix
,
idx
),
"功能描述"
:
req
.
title
,
"用例等级"
:
decide_priority
(
req
.
title
,
req
.
detail_lines
),
"功能编号"
:
req
.
code
or
""
,
"用例名称"
:
req
.
title
,
"预置条件"
:
"1. 环境已部署; 2. 具备执行权限; 3. 必要依赖/配置已准备"
,
"操作步骤"
:
build_steps
(
req
.
detail_lines
),
"JSON"
:
""
,
"预期结果"
:
build_expected
(
req
.
title
,
req
.
detail_lines
),
"测试结果"
:
""
,
"测试结论"
:
""
,
"日志截屏"
:
""
,
"备注"
:
""
,
}
# 确保所有字段存在
for
k
in
headers_order
:
record
.
setdefault
(
k
,
""
)
return
record
```
---
#### 任务 3.7:PRD 转用例(主函数)
**目标**
:读取 PRD 文件并转换为用例列表
**实现要点**
:
```
python
def
prd_to_cases
(
prd_path
:
str
,
module
:
str
,
prefix
:
str
,
start_idx
:
int
=
1
)
->
List
[
Dict
[
str
,
str
]]:
with
open
(
prd_path
,
"r"
,
encoding
=
"utf-8"
)
as
f
:
md
=
normalize_md
(
f
.
read
())
items
=
extract_requirement_items
(
md
)
cases
:
List
[
Dict
[
str
,
str
]]
=
[]
idx
=
start_idx
for
it
in
items
:
# 过滤无效标题
if
any
(
k
in
it
.
title
for
k
in
[
"说明"
,
"目录"
,
"背景"
,
"概述"
]):
continue
cases
.
append
(
to_case_record
(
idx
=
idx
,
module
=
module
,
prefix
=
prefix
,
req
=
it
))
idx
+=
1
return
cases
```
**关键点**
:
-
`start_idx`
支持多 PRD 全局递增
-
过滤"说明、目录、背景、概述"等无效标题
---
### 2.4 阶段四:Excel 写入(优先级:高)
#### 任务 4.1:表头查找
**目标**
:在模板中查找表头行
**实现要点**
:
```
python
def
find_header_row
(
template_sheet
)
->
int
:
must_keys
=
{
"序号"
,
"用例名称"
,
"操作步骤"
,
"预期结果"
}
max_scan
=
min
(
30
,
template_sheet
.
max_row
or
30
)
for
r
in
range
(
1
,
max_scan
+
1
):
values
=
[]
for
c
in
range
(
1
,
min
(
40
,
template_sheet
.
max_column
or
40
)
+
1
):
v
=
template_sheet
.
cell
(
row
=
r
,
column
=
c
)
.
value
if
v
is
None
:
continue
values
.
append
(
str
(
v
)
.
strip
())
# 至少匹配 2 个关键字
if
len
(
must_keys
.
intersection
(
values
))
>=
2
:
return
r
return
3
# 默认第 3 行
```
**关键点**
:
-
扫描前 30 行
-
匹配至少 2 个关键字
-
默认返回 3
---
#### 任务 4.2:Sheet 名称安全处理
**目标**
:确保 Sheet 名称符合 Excel 规范
**实现要点**
:
```
python
def
safe_sheet_name
(
name
:
str
)
->
str
:
# Excel 不允许的字符: [ ] : * ? / \
name
=
re
.
sub
(
r"[\[\]\:\*\?\/\\]"
,
"_"
,
name
)
return
name
[:
31
]
# 最大 31 字符
```
**Excel Sheet 名称限制**
:
-
最大长度:31 字符
-
不允许字符:
`[ ] : * ? / \`
---
#### 任务 4.3:写入 Sheet
**目标**:将用例写入 Excel Sheet
**实现要点**:
```python
from copy import copy
from openpyxl.styles import Alignment
def write_cases_to_sheet(wb, template_sheet, sheet_name: str, cases: List[Dict[str, str]]):
# Sheet 存在检查
if sheet_name in wb.sheetnames:
ws = wb[sheet_name]
start_row = (ws.max_row or 1) + 1 # 追加模式
else:
ws = wb.create_sheet(sheet_name)
start_row = 2
# 复制表头
header_row_index = find_header_row(template_sheet)
for col_idx, cell in enumerate(template_sheet[header_row_index], start=1):
new_cell = ws.cell(row=1, column=col_idx, value=cell.value)
if cell.has_style:
new_cell.font = copy(cell.font)
new_cell.fill = copy(cell.fill)
new_cell.border = copy(cell.border)
new_cell.alignment = copy(cell.alignment)
new_cell.number_format = cell.number_format
ws.freeze_panes = "B2" # 冻结首行
# 写入数据
row = start_row
for case in cases:
for col_idx, header in enumerate(headers_order, start=1):
val = case.get(header, "")
# 分号转换行(预置条件、操作步骤)
if header in ("预置条件", "操作步骤") and isinstance(val, str):
val = val.replace("; ", "\n")
# JSON 强制清空
if header == "JSON":
val = ""
ws.cell(row=row, column=col_idx, value=val)
row += 1
# 自动换行(预置条件、操作步骤)
col_idx_pre = headers_order.index("预置条件") + 1
col_idx_steps = headers_order.index("操作步骤") + 1
for r in range(1, row):
ws.cell(row=r, column=col_idx_pre).alignment = Alignment(wrap_text=True, vertical="top")
ws.cell(row=r, column=col_idx_steps).alignment = Alignment(wrap_text=True, vertical="top")
# 列宽自适应
for col in ws.columns:
max_len = 0
col_letter = col[0].column_letter
for c in col:
v = c.value
if v is None:
continue
l = len(str(v))
if l > max_len:
max_len = l
ws.column_dimensions[col_letter].width = min(max_len + 2, 60)
return ws
```
**关键点**:
1. Sheet 复用:存在则追加,不存在则创建
2. 表头样式复制:font、fill、border、alignment、number_format
3. 分号转换行:预置条件、操作步骤
4. JSON 强制清空
5. 自动换行:预置条件、操作步骤列
6. 列宽最大 60
---
### 2.5 阶段五:交互式操作(优先级:中)
#### 任务 5.1:列出 PRD 文件
**目标**:列出 PRD 目录下的所有 .md 文件
**实现要点**:
```python
def list_prd_files(prd_dir: str) -> List[str]:
if not os.path.isdir(prd_dir):
return []
fs = [f for f in os.listdir(prd_dir) if f.lower().endswith(".md")]
fs.sort()
return fs
```
---
#### 任务 5.2:解析多选输入
**目标**:解析用户输入的序号
**实现要点**:
```python
def parse_multi_input(s: str) -> List[str]:
s = (s or "").strip()
if not s:
return []
parts = re.split(r"[,\s]+", s) # 逗号或空格分隔
return [p for p in (x.strip() for x in parts) if p]
```
**支持格式**:`
1,2,5
` 或 `
1 2 5
` 或 `
1, 2, 5
`
---
#### 任务 5.3:交互式选择 PRD
**目标**:引导用户选择 PRD 文件
**实现要点**:
```python
def pick_prds_interactively(prd_dir: str) -> List[str]:
files = list_prd_files(prd_dir)
if not files:
raise RuntimeError(f"PRD目录为空或不存在:{prd_dir}")
print(f"PRD目录:{prd_dir}")
for i, f in enumerate(files, start=1):
print(f"{i:>2}. {f}")
print("支持多选:输入多个序号(如 1,2,5 或 1 2 5)。")
while True:
s = input("请输入PRD序号(可多选):").strip()
parts = parse_multi_input(s)
if not parts or not all(p.isdigit() for p in parts):
print("输入无效,请输入序号(可多选)。")
continue
idxs: List[int] = []
ok = True
for p in parts:
n = int(p)
if not (1 <= n <= len(files)):
ok = False
break
idxs.append(n)
if not ok:
print("序号超出范围,请重试。")
continue
# 去重并构建路径
seen = set()
paths: List[str] = []
for n in idxs:
if n in seen:
continue
seen.add(n)
paths.append(os.path.join(prd_dir, files[n - 1]))
return paths
```
**关键点**:
1. 空目录检测
2. 输入验证(数字、范围)
3. 自动去重
---
#### 任务 5.4:输入 Sheet 名称
**目标**:获取 Sheet 名称并安全处理
**实现要点**:
```python
def ask_sheet_name_interactively(default_name: str) -> str:
s = input(f"请输入Sheet名称(回车使用默认:{default_name}):").strip()
return safe_sheet_name(s or default_name)
```
---
#### 任务 5.5:输入输出文件名
**目标**:获取输出文件名
**实现要点**:
```python
def ask_output_name_interactively(default_name: str) -> str:
s = input(f"请输入生成的测试用例文件名称(回车使用默认:{default_name}):").strip()
return s or default_name
```
---
### 2.6 阶段六:主流程集成(优先级:高)
#### 任务 6.1:主函数
**目标**:整合所有模块,实现完整流程
**实现要点**:
```python
from datetime import datetime
def main():
# 1. 解析命令行参数
parser = argparse.ArgumentParser(description="根据PRD生成用例JSON,并写入测试用例Excel")
parser.add_argument("--template", default=TEMPLATE_PATH_DEFAULT, help="模板Excel路径")
parser.add_argument("--module", default="通用模块", help="功能模块字段值")
parser.add_argument("--prefix", default="TC", help="用例编号前缀")
parser.add_argument("--overwrite", action="store_true", help="是否覆盖保存")
args = parser.parse_args()
# 2. 定位 PRD 目录
prd_dir = resolve_prd_dir()
# 3. 检查模板文件
if not os.path.exists(args.template):
print("找不到模板文件:", args.template)
return
# 4. 交互式选择 PRD
prd_paths = pick_prds_interactively(prd_dir)
# 5. 确定 Sheet 名称
if len(prd_paths) == 1:
default_sheet = f"{os.path.splitext(os.path.basename(prd_paths[0]))[0]}_用例"
else:
default_sheet = "多PRD_用例"
sheet_name = ask_sheet_name_interactively(default_sheet)
# 6. 确定输出文件名
if len(prd_paths) == 1:
default_out_base = os.path.splitext(os.path.basename(prd_paths[0]))[0]
else:
default_out_base = "MultiPRD"
out_base = ask_output_name_interactively(default_out_base)
# 7. 打开模板
wb = load_workbook(args.template)
template_sheet = wb.worksheets[0]
# 8. 多 PRD 合并到同一个 sheet(序号全局递增)
os.makedirs(OUTPUT_JSON_DIR, exist_ok=True)
total_cases = 0
next_idx = 1
for prd_path in prd_paths:
prd_base = os.path.splitext(os.path.basename(prd_path))[0]
# 生成用例
cases = prd_to_cases(prd_path=prd_path, module=args.module, prefix=args.prefix, start_idx=next_idx)
if not cases:
print(f"未从PRD抽取到可生成用例的条目:{prd_path}")
continue
# 输出 JSON
json_path = os.path.join(OUTPUT_JSON_DIR, f"{prd_base}_用例.json")
with open(json_path, "w", encoding="utf-8") as f:
json.dump(cases, f, ensure_ascii=False, indent=4)
print(f"已生成 JSON:{json_path}({len(cases)}条)")
# 写入 Sheet
write_cases_to_sheet(wb, template_sheet, sheet_name, cases)
total_cases += len(cases)
next_idx += len(cases)
# 9. 保存 Excel
if args.overwrite:
out_xlsx = args.template
else:
os.makedirs(OUTPUT_XLSX_DIR, exist_ok=True)
ts = datetime.now().strftime("%Y%m%d_%H%M%S")
out_xlsx = os.path.join(OUTPUT_XLSX_DIR, f"{out_base}_{ts}.xlsx")
wb.save(out_xlsx)
print("已生成Excel:", out_xlsx)
print("Sheet:", sheet_name)
print("总用例条数:", total_cases)
if __name__ == "__main__":
main()
```
---
## 3. 测试验证
### 3.1 单元测试
| 模块 | 测试项 | 预期结果 |
|------|--------|----------|
| `
resolve_prd_dir()
` | 目录存在检测 | 返回存在的目录路径 |
| `
normalize_md()
` | 代码块过滤 | ````...```` 被移除 |
| `
normalize_md()
` | HTML 注释过滤 | `
<!--...-->
` 被移除 |
| `
extract_requirement_items()
` | 点分编号解析 | 正确提取 code 和 title |
| `
extract_requirement_items()
` | Markdown 标题解析 | 正确提取 title |
| `
extract_requirement_items()
` | 嵌套标题处理 | 嵌套标题作为详情 |
| `
classify_category()
` | 异常场景 | 包含"异常"返回"异常场景" |
| `
classify_category()
` | 安全/鉴权 | 包含"安全"返回"安全/鉴权" |
| `
decide_priority()
` | 高等级 | 包含"必须"返回"高" |
| `
build_steps()
` | 无匹配内容 | 返回默认值 |
| `
build_steps()
` | 有匹配内容 | 返回分号分隔的步骤 |
| `
build_expected()
` | 无匹配内容 | 返回默认值 |
| `
safe_sheet_name()
` | 非法字符过滤 | `
[
]
:
*
? /
\`
替换为
`_`
|
|
`safe_sheet_name()`
| 长度限制 | 超过 31 字符截断 |
|
`find_header_row()`
| 表头查找 | 返回正确行号 |
### 3.2 集成测试
| 场景 | 操作 | 预期结果 |
|------|------|----------|
| 单 PRD 生成 | 选择 1 个 PRD | 生成 JSON 和 Excel |
| 多 PRD 合并 | 选择多个 PRD | 合并在同一 Sheet,序号递增 |
| Sheet 复用 | 重复生成到同一 Sheet | 数据追加而非覆盖 |
| 覆盖模式 | 使用
`--overwrite`
| 覆盖模板文件 |
| 命令行参数 | 使用
`--module`
、
`--prefix`
| 参数生效 |
### 3.3 边界测试
| 场景 | 操作 | 预期结果 |
|------|------|----------|
| 空 PRD 目录 | 无 .md 文件 | 报错提示 |
| 模板文件不存在 | 指定不存在的模板 | 报错提示 |
| 无效输入 | 输入非数字序号 | 提示重试 |
| 序号超范围 | 输入超出范围的序号 | 提示重试 |
| 无可用条目 | PRD 无有效需求 | 跳过该 PRD |
---
## 4. 依赖关系
### 4.1 模块依赖图
```
main()
├── argparse (命令行解析)
├── resolve_prd_dir() (目录定位)
├── pick_prds_interactively()
│ └── list_prd_files()
│ └── parse_multi_input()
├── ask_sheet_name_interactively()
│ └── safe_sheet_name()
├── ask_output_name_interactively()
├── load_workbook() (openpyxl)
├── prd_to_cases()
│ ├── normalize_md()
│ ├── extract_requirement_items()
│ │ ├── clean_line()
│ │ └── RequirementItem
│ └── to_case_record()
│ ├── classify_category()
│ ├── decide_priority()
│ ├── build_steps()
│ ├── build_expected()
│ └── make_case_id()
├── write_cases_to_sheet()
│ ├── find_header_row()
│ └── Alignment() (openpyxl)
└── wb.save() (openpyxl)
```
### 4.2 开发顺序建议
1.
**第一批**
:基础框架
-
路径与常量定义
-
命令行参数解析
-
数据类定义
2.
**第二批**
:PRD 解析
-
正则表达式定义
-
Markdown 预处理
-
需求条目抽取
-
行清洗
3.
**第三批**
:用例生成
-
功能类别判定
-
用例等级判定
-
操作步骤生成
-
预期结果生成
-
用例编号生成
-
用例记录生成
-
PRD 转用例主函数
4.
**第四批**
:Excel 写入
-
表头查找
-
Sheet 名称安全处理
-
写入 Sheet
5.
**第五批**
:交互式操作
-
列出 PRD 文件
-
解析多选输入
-
交互式选择 PRD
-
输入处理函数
6.
**第六批**
:主流程集成
-
主函数
---
## 5. 关键常量与配置
### 5.1 正则表达式
| 变量 | 表达式 | 用途 |
|------|--------|------|
|
`RE_SECTION`
|
`^\s*(\d+(?:\.\d+)*)\s*[、\.\-]\s*(.+?)\s*[::]?\s*$`
| 匹配点分编号 |
|
`RE_MD_HEADING`
|
`^\s*(#{1,6})\s+(.+?)\s*$`
| 匹配 Markdown 标题 |
### 5.2 关键词列表
| 用途 | 关键词 |
|------|--------|
| 异常场景 |
`["异常", "失败", "错误", "告警", "报警", "超时", "断开", "暴涨"]`
|
| 安全/鉴权 |
`["安全", "权限", "鉴权", "加密", "脱敏"]`
|
| 运维可观测 |
`["报告", "输出", "word", "markdown", "邮件", "钉钉", "通知"]`
|
| 高等级 |
`["必须", "报警", "告警", "峰值", "暴涨", "发送", "对接"]`
|
| 中等级 |
`["待实现", "可以", "建议", "优化"]`
|
| 操作步骤 |
`["检查", "监测", "记录", "输出", "发送", "查询", "进入", "生成", "判定", "调用", "执行"]`
|
| 预期结果 |
`["需要", "应", "必须", "输出", "记录", "发送", "判定", "成功", "失败"]`
|
| 过滤标题 |
`["说明", "目录", "背景", "概述"]`
|
### 5.3 默认值
| 配置项 | 默认值 |
|--------|--------|
| 模板文件 |
`兰州中石化项目测试用例20251203.xlsx`
|
| 功能模块 |
`通用模块`
|
| 用例编号前缀 |
`TC`
|
| 预置条件 |
`1. 环境已部署; 2. 具备执行权限; 3. 必要依赖/配置已准备`
|
| 操作步骤默认值 |
`1. 按 PRD 描述执行该功能/流程; 2. 采集相关日志/输出; 3. 记录实际结果`
|
| 预期结果默认值 |
`{标题} 按 PRD 约定产出正确结果(请补充具体断言点)`
|
| Sheet 单 PRD 默认 |
`{PRD文件名}_用例`
|
| Sheet 多 PRD 默认 |
`多PRD_用例`
|
| 输出文件单 PRD 默认 |
`{PRD文件名}`
|
| 输出文件多 PRD 默认 |
`MultiPRD`
|
### 5.4 限制值
| 配置项 | 限制值 |
|--------|--------|
| Sheet 名称最大长度 | 31 字符 |
| 表头扫描最大行数 | 30 行 |
| 表头扫描最大列数 | 40 列 |
| 操作步骤最大条数 | 6 条 |
| 预期结果最大条数 | 6 条 |
| 列宽最大值 | 60 |
| 标题最小长度 | 2 字符 |
---
## 6. 验收清单
### 6.1 功能验收
-
[
]
能正确解析点分编号格式(如
`1.1`
、
`2.3.1`
)
-
[
]
能正确解析 Markdown 标题格式(如
`## 标题`
)
-
[
]
能处理 Markdown 标题嵌套场景
-
[
]
功能类别判定符合规则
-
[
]
用例等级判定符合规则
-
[
]
操作步骤生成符合规则(分号分隔、最多 6 条)
-
[
]
预期结果生成符合规则(分号分隔、最多 6 条)
-
[
]
支持单 PRD 生成
-
[
]
支持多 PRD 合并(序号全局递增)
-
[
]
Sheet 复用功能正常(追加模式)
-
[
]
JSON 文件正确生成
-
[
]
Excel 文件正确生成
-
[
]
分号在 Excel 中转换为换行
-
[
]
JSON 列在 Excel 中强制清空
-
[
]
Sheet 名称非法字符被过滤
-
[
]
Sheet 名称长度限制为 31 字符
-
[
]
命令行参数生效
-
[
]
覆盖模式正常工作
### 6.2 异常验收
-
[
]
PRD 目录为空时正确报错
-
[
]
模板文件不存在时正确报错
-
[
]
无效输入时提示重试
-
[
]
序号超范围时提示重试
-
[
]
无可用需求条目时跳过该 PRD
-
[
]
写入失败时有明确提示
---
## 7. 参考资料
### 7.1 需求文档
-
`_PRD_根据PRD文档生成测试用例.md`
### 7.2 现有实现
-
`AuxiliaryTool/用例生成/create_test_xlsx.py`
### 7.3 依赖库
-
`openpyxl`
:Excel 文件操作
-
Python 标准库:
`os`
,
`re`
,
`json`
,
`argparse`
,
`datetime`
,
`dataclasses`
,
`typing`
,
`copy`
### 7.4 需求规范
-
代码规范:
`Docs/PRD/01规范文档/_PRD_规范文档_代码规范.md`
-
问题总结:
`Docs/PRD/01规范文档/_PRD_问题总结_记录文档.md`
-
方法总结:
`Docs/PRD/01规范文档/_PRD_方法总结_记录文档.md`
-
文档规范:
`Docs/PRD/01规范文档/_PRD_规范文档_文档规范.md`
-
测试规范:
`Docs/PRD/01规范文档/_PRD_规范文档_测试规范.md`
Docs/PRD/自动生成功能测试报告/自动生成功能报告需求文档.md
deleted
100644 → 0
浏览文件 @
e50d943e
编写
预览
Markdown
格式
0%
重试
或
添加新文件
添加附件
取消
您添加了
0
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论