测试用例生成器(testcase-generator) 1. 目标 根据测试点生成结构化测试用例,覆盖正向、反向、边界场景。 2. 输入输出 - 输入 : 、 、 - 输出 : 、 3. 核心原则 1. 策略指导 :提供判断标准,而非强制数量 2. 场景驱动 :基于实际场景复杂度生成用例 3. 数据具体 :测试数据必须具体,不用占位符 4. 可验证性 :预期结果必须明确可验证 --- 触发条件 - 用户执行 命令 - 存在 --- 4. 核心理论 4.1 等价类划分法 将输入数据划分为若干等价类,从每类中选取代表性数据测试。 应用示例 : 4.2 边界值分析法 边界是最容易出错的地方。 边界三点法 : 离点(min-1) | 上点(min) | 内点(中间值) | 上点(max) | 离点(max+1) 应用示例 : 4.3 用例数量判断 不强制数量,根据场景复杂度自适应调整。 判断标准 : - 输入项数量:输入项越多,用例越多 - 业务规则复杂度:规则越复杂,用例越多 - 风险等级:Critical 和 High 需要更多用例 - 测试关注点:关注点越多,用例越多 --- 5. 用例格式规范 5.1 Markdown文本协议格式 (v0.2) 每个测试点生成一个 文件,格式如下: 5.2 字段说明 - 优先级 :P1-P5(必填) - P1: 核心功能正向流程 - P2: 基本…

)\n FIELD_PATTERN = re.compile(r'^\\[([^\\]]+)\\]\\s*(.*)

测试用例生成器(testcase-generator) 1. 目标 根据测试点生成结构化测试用例,覆盖正向、反向、边界场景。 2. 输入输出 - 输入 : 、 、 - 输出 : 、 3. 核心原则 1. 策略指导 :提供判断标准,而非强制数量 2. 场景驱动 :基于实际场景复杂度生成用例 3. 数据具体 :测试数据必须具体,不用占位符 4. 可验证性 :预期结果必须明确可验证 --- 触发条件 - 用户执行 命令 - 存在 --- 4. 核心理论 4.1 等价类划分法 将输入数据划分为若干等价类,从每类中选取代表性数据测试。 应用示例 : 4.2 边界值分析法 边界是最容易出错的地方。 边界三点法 : 离点(min-1) | 上点(min) | 内点(中间值) | 上点(max) | 离点(max+1) 应用示例 : 4.3 用例数量判断 不强制数量,根据场景复杂度自适应调整。 判断标准 : - 输入项数量:输入项越多,用例越多 - 业务规则复杂度:规则越复杂,用例越多 - 风险等级:Critical 和 High 需要更多用例 - 测试关注点:关注点越多,用例越多 --- 5. 用例格式规范 5.1 Markdown文本协议格式 (v0.2) 每个测试点生成一个 文件,格式如下: 5.2 字段说明 - 优先级 :P1-P5(必填) - P1: 核心功能正向流程 - P2: 基本…

)\n\n # 步骤分隔符(支持多种格式)\n STEP_SEPARATORS = ['。', ';', ',', '\\n']\n\n # 有效的测试类型\n VALID_TEST_TYPES = [\n \"功能\", \"兼容性\", \"易用性\", \"性能\", \"稳定性\",\n \"安全性\", \"可靠性\", \"效果(AI类、资源类)\",\n \"效果(硬件器件类)\", \"可维护性\", \"可移植性\", \"埋点\"\n ]\n\n def __init__(self, strict: bool = True):\n \"\"\"\n 初始化解析器\n\n Args:\n strict: 是否严格模式(严格模式下会对所有错误抛出异常)\n \"\"\"\n self.strict = strict\n self.errors: List[ParseError] = []\n\n def parse_file(self, file_path: Path) -> List[TestCase]:\n \"\"\"\n 解析测试用例文件\n\n Args:\n file_path: 文件路径\n\n Returns:\n 测试用例列表\n \"\"\"\n try:\n with open(file_path, 'r', encoding='utf-8') as f:\n content = f.read()\n except Exception as e:\n raise ParseError(f\"无法读取文件: {e}\")\n\n return self.parse_content(content)\n\n def parse_content(self, content: str) -> List[TestCase]:\n \"\"\"\n 解析测试用例内容\n\n Args:\n content: 文件内容\n\n Returns:\n 测试用例列表\n \"\"\"\n lines = content.split('\\n')\n cases = []\n\n i = 0\n while i \u003c len(lines):\n line = lines[i].strip()\n\n # 查找用例标题(## 开头)\n if line.startswith('##'):\n case, next_i = self._parse_case(lines, i)\n if case:\n cases.append(case)\n i = next_i\n else:\n i += 1\n\n return cases\n\n def _parse_case(self, lines: List[str], start_idx: int) -> Tuple[Optional[TestCase], int]:\n \"\"\"\n 解析单个用例\n\n Args:\n lines: 所有行\n start_idx: 起始行索引\n\n Returns:\n (用例对象, 下一个用例的起始索引)\n \"\"\"\n # 解析标题\n title_line = lines[start_idx].strip()\n line_num = start_idx + 1\n\n try:\n priority, is_negative, title = self._parse_title(title_line, line_num)\n except ParseError as e:\n if self.strict:\n raise\n self.errors.append(e)\n return None, start_idx + 1\n\n # 收集字段内容(直到下一个 ## 或文件结束)\n fields = {}\n i = start_idx + 1\n current_field = None\n current_content = []\n\n while i \u003c len(lines):\n line = lines[i].strip()\n\n # 遇到下一个用例,结束当前用例\n if line.startswith('##'):\n break\n\n # 跳过空行\n if not line:\n i += 1\n continue\n\n # 检查是否是字段标签\n match = self.FIELD_PATTERN.match(line)\n if match:\n # 保存上一个字段\n if current_field:\n fields[current_field] = '\\n'.join(current_content).strip()\n\n # 开始新字段\n current_field = match.group(1)\n current_content = [match.group(2)] if match.group(2) else []\n else:\n # 多行内容\n if current_field:\n current_content.append(line)\n\n i += 1\n\n # 保存最后一个字段\n if current_field:\n fields[current_field] = '\\n'.join(current_content).strip()\n\n # 构建用例对象\n try:\n case = self._build_case(priority, is_negative, title, fields, line_num, title_line)\n self._validate_case(case, line_num)\n return case, i\n except ParseError as e:\n if self.strict:\n raise\n self.errors.append(e)\n return None, i\n\n def _parse_title(self, title_line: str, line_num: int) -> Tuple[str, bool, str]:\n \"\"\"\n 解析用例标题\n\n Args:\n title_line: 标题行\n line_num: 行号\n\n Returns:\n (优先级, 是否反向用例, 标题文本)\n \"\"\"\n match = self.TITLE_PATTERN.match(title_line)\n if not match:\n raise ParseError(\n f\"用例标题格式错误,应为 '## [P1..P5][反向可选] 标题',实际: {title_line}\",\n line_num\n )\n\n priority = match.group(1)[1:-1] # 去掉方括号,如 [P1] -> P1\n is_negative = match.group(2) is not None\n title = match.group(3).strip()\n\n return priority, is_negative, title\n\n def _parse_steps(self, steps_str: str, line_num: int) -> List[str]:\n \"\"\"\n 解析步骤字符串\n\n 支持格式:\n - 1. xxx。2. xxx\n - 1. xxx;2. xxx\n - 1. xxx\\n2. xxx\n\n Args:\n steps_str: 步骤字符串\n line_num: 行号\n\n Returns:\n 步骤列表\n \"\"\"\n if not steps_str:\n return []\n\n # 先尝试按换行分割\n if '\\n' in steps_str:\n lines = steps_str.strip().split('\\n')\n steps = []\n for line in lines:\n m = re.match(r'(\\d+)\\.\\s*(.+)', line.strip())\n if m:\n num = int(m.group(1))\n if num != len(steps) + 1:\n raise ParseError(f\"步骤编号不连续,期望 {len(steps) + 1},实际 {num}\", line_num)\n steps.append(m.group(2).strip())\n if steps:\n return steps\n\n # 使用更精确的正则:步骤编号必须在开头或分隔符后\n # 匹配模式:开头或分隔符后的 \"数字. \"\n # 关键:步骤编号前面必须是开头、分隔符(。;,)或空格,而不是其他字符\n \n steps = []\n # 使用 findall 找到所有步骤\n # 模式:(^|[。;,\\s])(\\d+)\\.\\s*([^。;,]+(?:[。;,]|$))\n # 更简单的方法:按步骤编号分割,但要确保编号在正确位置\n \n # 先找到所有步骤编号的位置\n # 步骤编号特征:在开头或分隔符后,后面跟 \". \"\n pattern = r'(?:^|[。;,])\\s*(\\d+)\\.\\s*'\n \n # 找到所有匹配\n matches = list(re.finditer(pattern, steps_str))\n \n if not matches:\n # 尝试简单格式:只有一个步骤,没有编号\n # 或者格式不标准,返回整个字符串作为一个步骤\n raise ParseError(f\"无法解析步骤格式: {steps_str[:50]}...\", line_num)\n \n for idx, match in enumerate(matches):\n num = int(match.group(1))\n if num != len(steps) + 1:\n raise ParseError(f\"步骤编号不连续,期望 {len(steps) + 1},实际 {num}\", line_num)\n \n # 获取步骤内容:从当前匹配结束到下一个匹配开始(或字符串结束)\n start = match.end()\n if idx + 1 \u003c len(matches):\n end = matches[idx + 1].start()\n else:\n end = len(steps_str)\n \n content = steps_str[start:end].strip().rstrip('。;,')\n if content:\n steps.append(content)\n\n if not steps:\n raise ParseError(f\"无法解析步骤格式: {steps_str[:50]}...\", line_num)\n\n return steps\n\n def _build_case(self, priority: str, is_negative: bool, title: str,\n fields: Dict[str, str], line_num: int, raw_title: str) -> TestCase:\n \"\"\"\n 构建用例对象\n\n Args:\n priority: 优先级\n is_negative: 是否反向用例(从标题提取)\n title: 标题\n fields: 字段字典\n line_num: 行号\n raw_title: 原始标题行\n\n Returns:\n 测试用例对象\n \"\"\"\n # 解析步骤和预期结果\n raw_steps = fields.get('测试步骤', '')\n raw_expected = fields.get('预期结果', '')\n\n steps = self._parse_steps(raw_steps, line_num)\n expected = self._parse_steps(raw_expected, line_num)\n\n # 反向用例标记:优先从标题提取,其次从字段\n if '反向用例' in fields:\n is_negative = fields['反向用例'] in ['是', '是']\n\n # 构建用例\n case = TestCase(\n priority=priority,\n is_negative=is_negative,\n title=title,\n test_type=fields.get('测试类型', ''),\n steps=steps,\n expected=expected,\n precondition=fields.get('前置条件', ''),\n note=fields.get('备注', ''),\n line_number=line_num,\n raw_title=raw_title,\n raw_steps=raw_steps,\n raw_expected=raw_expected\n )\n\n return case\n\n def _validate_case(self, case: TestCase, line_num: int):\n \"\"\"\n 验证用例完整性\n\n Args:\n case: 测试用例对象\n line_num: 行号\n \"\"\"\n # 验证必填字段\n if not case.test_type:\n raise ParseError(\"缺少必填字段 [测试类型]\", line_num)\n\n if not case.steps:\n raise ParseError(\"缺少必填字段 [测试步骤]\", line_num)\n\n if not case.expected:\n raise ParseError(\"缺少必填字段 [预期结果]\", line_num)\n\n # 验证测试类型\n if case.test_type not in self.VALID_TEST_TYPES:\n raise ParseError(\n f\"无效的测试类型: {case.test_type},必须是以下之一: {', '.join(self.VALID_TEST_TYPES)}\",\n line_num\n )\n\n # 验证步骤和预期结果数量一致\n if len(case.steps) != len(case.expected):\n raise ParseError(\n f\"测试步骤数量({len(case.steps)})与预期结果数量({len(case.expected)})不一致\",\n line_num\n )\n\n\ndef parse_test_cases(file_path: Path, strict: bool = True) -> List[TestCase]:\n \"\"\"\n 便捷函数:解析测试用例文件\n\n Args:\n file_path: 文件路径\n strict: 是否严格模式\n\n Returns:\n 测试用例列表\n \"\"\"\n parser = TextProtocolParser(strict=strict)\n return parser.parse_file(file_path)\n\n\n# 测试代码\nif __name__ == \"__main__\":\n import sys\n\n if len(sys.argv) \u003c 2:\n print(\"用法: python parse_text_protocol.py \u003c文件路径>\")\n sys.exit(1)\n\n file_path = Path(sys.argv[1])\n try:\n cases = parse_test_cases(file_path)\n print(f\"✅ 解析成功,共 {len(cases)} 个用例\\n\")\n for i, case in enumerate(cases, 1):\n print(f\"用例 {i}: [{case.priority}] {case.title}\")\n print(f\" 测试类型: {case.test_type}\")\n print(f\" 反向用例: {'是' if case.is_negative else '否'}\")\n print(f\" 步骤数: {len(case.steps)}\")\n for j, (step, exp) in enumerate(zip(case.steps, case.expected), 1):\n print(f\" {j}. {step} → {exp}\")\n if case.precondition:\n print(f\" 前置条件: {case.precondition}\")\n if case.note:\n print(f\" 备注: {case.note}\")\n print()\n except ParseError as e:\n print(f\"❌ 解析失败: {e}\")\n sys.exit(1)\n","content_type":"text/x-python; charset=utf-8","language":"python","size":14026,"content_sha256":"aa3eb520a45842a72eff4acf7d04c46341e2b2c92849a4cb6403c5cdc20bd4b4"},{"filename":"scripts/to_excel.py","content":"#!/usr/bin/env python3\n\"\"\"\n测试用例导出 Excel 工具(v0.2)\n\n基于 docs/specs/测试用例导入规范.md 和 docs/specs/测试用例文本协议.md 规范\n\n输出格式严格遵循导入规范定义的字段:\n- 必填:优先级、用例标题、操作步骤、预期结果、测试类型\n- 可选:一级分组、二级分组、前置条件、是否反向用例、备注\n\n用法:\n python to_excel.py test-case/all_cases.md -o output.xlsx\n python to_excel.py test-case/ -o output.xlsx\n\"\"\"\n\nimport sys\nimport argparse\nfrom pathlib import Path\nfrom typing import List, Dict\n\ntry:\n from openpyxl import Workbook\n from openpyxl.styles import Font, Alignment, PatternFill, Border, Side\n from openpyxl.utils import get_column_letter\nexcept ImportError:\n print(\"❌ 需要安装 openpyxl: `uv add openpyxl`\")\n sys.exit(1)\n\nfrom parse_text_protocol import parse_test_cases, TestCase\n\n\n# Excel 列配置(严格遵循 测试用例导入规范.md)\nEXCEL_COLUMNS = [\n # 必填字段\n {\"field\": \"优先级\", \"width\": 8, \"center\": True},\n {\"field\": \"用例标题\", \"width\": 40},\n {\"field\": \"操作步骤\", \"width\": 50, \"wrap\": True},\n {\"field\": \"预期结果\", \"width\": 40, \"wrap\": True},\n {\"field\": \"测试类型\", \"width\": 12, \"center\": True},\n # 可选字段\n {\"field\": \"前置条件\", \"width\": 25},\n {\"field\": \"是否反向用例\", \"width\": 12, \"center\": True},\n {\"field\": \"一级分组\", \"width\": 18},\n {\"field\": \"二级分组\", \"width\": 18},\n {\"field\": \"备注\", \"width\": 20},\n]\n\n\nclass ExcelExporter:\n \"\"\"Excel 导出器\"\"\"\n\n def __init__(self, sheet_name: str = \"测试用例\"):\n self.sheet_name = sheet_name\n self.wb = Workbook()\n self.ws = self.wb.active\n self.ws.title = sheet_name\n\n def export(self, rows: List[Dict], output_file: Path):\n \"\"\"导出测试用例到 Excel\"\"\"\n self._write_header()\n self._write_data(rows)\n self._apply_styles()\n self.wb.save(output_file)\n print(f\"✅ 导出成功: {output_file}\")\n print(f\" 总计 {len(rows)} 个用例\")\n\n def _write_header(self):\n \"\"\"写入表头\"\"\"\n for col_idx, col_config in enumerate(EXCEL_COLUMNS, 1):\n cell = self.ws.cell(row=1, column=col_idx)\n cell.value = col_config[\"field\"]\n cell.font = Font(bold=True, color=\"FFFFFF\", size=11)\n cell.fill = PatternFill(start_color=\"366092\", end_color=\"366092\", fill_type=\"solid\")\n cell.alignment = Alignment(horizontal=\"center\", vertical=\"center\")\n self.ws.column_dimensions[get_column_letter(col_idx)].width = col_config[\"width\"]\n\n def _write_data(self, rows: List[Dict]):\n \"\"\"写入数据\"\"\"\n for row_idx, row_data in enumerate(rows, 2):\n for col_idx, col_config in enumerate(EXCEL_COLUMNS, 1):\n field = col_config[\"field\"]\n value = row_data.get(field, \"\")\n cell = self.ws.cell(row=row_idx, column=col_idx)\n cell.value = value\n\n if col_config.get(\"center\"):\n cell.alignment = Alignment(horizontal=\"center\", vertical=\"center\")\n elif col_config.get(\"wrap\"):\n cell.alignment = Alignment(wrap_text=True, vertical=\"top\")\n else:\n cell.alignment = Alignment(vertical=\"top\")\n\n def _apply_styles(self):\n \"\"\"应用边框样式\"\"\"\n thin_border = Border(\n left=Side(style='thin'),\n right=Side(style='thin'),\n top=Side(style='thin'),\n bottom=Side(style='thin')\n )\n for row in self.ws.iter_rows(min_row=1, max_row=self.ws.max_row, \n min_col=1, max_col=len(EXCEL_COLUMNS)):\n for cell in row:\n cell.border = thin_border\n\n\ndef case_to_row(case: TestCase, item: str = \"\", point: str = \"\") -> Dict:\n \"\"\"\n 将测试用例对象转换为 Excel 行数据\n \n 严格遵循 测试用例导入规范.md 的字段定义\n \"\"\"\n # 操作步骤和预期结果:保持原始格式(带编号)\n # 如果有原始字符串,优先使用;否则重新构建\n if case.raw_steps:\n steps_str = case.raw_steps\n else:\n steps_str = \"。\".join([f\"{i}. {step}\" for i, step in enumerate(case.steps, 1)])\n \n if case.raw_expected:\n expected_str = case.raw_expected\n else:\n expected_str = \"。\".join([f\"{i}. {exp}\" for i, exp in enumerate(case.expected, 1)])\n\n return {\n # 必填字段\n \"优先级\": case.priority,\n \"用例标题\": case.title,\n \"操作步骤\": steps_str,\n \"预期结果\": expected_str,\n \"测试类型\": case.test_type,\n # 可选字段\n \"前置条件\": case.precondition,\n \"是否反向用例\": \"是\" if case.is_negative else \"否\",\n \"一级分组\": item,\n \"二级分组\": point,\n \"备注\": case.note,\n }\n\n\ndef collect_from_file(file_path: Path, item: str = \"\", point: str = \"\") -> List[Dict]:\n \"\"\"从单个文件收集用例\"\"\"\n rows = []\n try:\n cases = parse_test_cases(file_path, strict=False)\n \n # 如果未指定 item/point,从路径推断\n if not item and not point:\n parts = file_path.parts\n if len(parts) >= 2:\n item = parts[-2]\n point = file_path.stem\n else:\n point = file_path.stem\n\n for case in cases:\n rows.append(case_to_row(case, item, point))\n \n if cases:\n print(f\"✅ {item}/{point}: {len(cases)} 个用例\")\n\n except Exception as e:\n print(f\"⚠️ 解析失败 {file_path}: {e}\")\n\n return rows\n\n\ndef collect_from_directory(dir_path: Path) -> List[Dict]:\n \"\"\"从目录收集所有用例\"\"\"\n all_rows = []\n\n for item_dir in dir_path.iterdir():\n if not item_dir.is_dir() or item_dir.name.startswith('.'):\n continue\n\n item_name = item_dir.name\n for md_file in item_dir.glob('*.md'):\n if md_file.name in ['plan.md', 'all_cases.md']:\n continue\n point_name = md_file.stem\n rows = collect_from_file(md_file, item_name, point_name)\n all_rows.extend(rows)\n\n return all_rows\n\n\ndef collect_from_merged_file(file_path: Path) -> List[Dict]:\n \"\"\"\n 从合并文件(all_cases.md)收集用例\n \n 合并文件格式:\n # 模块名\n ## 测试点名\n ## [P1] 用例标题\n ...\n \"\"\"\n rows = []\n current_item = \"\"\n current_point = \"\"\n \n try:\n with open(file_path, 'r', encoding='utf-8') as f:\n content = f.read()\n \n lines = content.split('\\n')\n i = 0\n while i \u003c len(lines):\n line = lines[i].strip()\n \n # 一级标题:模块名\n if line.startswith('# ') and not line.startswith('## '):\n current_item = line[2:].strip()\n i += 1\n continue\n \n # 二级标题:可能是测试点名或用例标题\n if line.startswith('## '):\n # 检查是否是用例标题(包含 [P1..P5])\n if '[P' in line and ']' in line:\n # 这是用例,需要解析\n # 收集用例内容直到下一个 ## 或 # 或 EOF\n case_lines = [line]\n i += 1\n while i \u003c len(lines):\n next_line = lines[i]\n if next_line.strip().startswith('#'):\n break\n case_lines.append(next_line)\n i += 1\n \n # 解析这个用例\n case_content = '\\n'.join(case_lines)\n from parse_text_protocol import TextProtocolParser\n parser = TextProtocolParser(strict=False)\n cases = parser.parse_content(case_content)\n \n for case in cases:\n rows.append(case_to_row(case, current_item, current_point))\n else:\n # 这是测试点名\n current_point = line[3:].strip()\n i += 1\n else:\n i += 1\n \n print(f\"✅ 从 {file_path.name} 解析: {len(rows)} 个用例\")\n \n except Exception as e:\n print(f\"⚠️ 解析失败 {file_path}: {e}\")\n \n return rows\n\n\ndef main():\n parser = argparse.ArgumentParser(description=\"将测试用例导出为 Excel 格式\")\n parser.add_argument(\"input\", help=\"输入文件或目录路径\")\n parser.add_argument(\"-o\", \"--output\", required=True, help=\"输出 Excel 文件路径\")\n args = parser.parse_args()\n\n input_path = Path(args.input)\n output_path = Path(args.output)\n\n if not input_path.exists():\n print(f\"❌ 路径不存在: {input_path}\")\n sys.exit(1)\n\n print(f\"开始导出测试用例: {input_path}\\n\")\n\n # 收集用例\n if input_path.is_file():\n if input_path.name == 'all_cases.md':\n rows = collect_from_merged_file(input_path)\n else:\n rows = collect_from_file(input_path)\n elif input_path.is_dir():\n rows = collect_from_directory(input_path)\n else:\n print(f\"❌ 无效的路径: {input_path}\")\n sys.exit(1)\n\n if not rows:\n print(\"⚠️ 没有找到任何测试用例\")\n sys.exit(0)\n\n # 导出\n exporter = ExcelExporter()\n exporter.export(rows, output_path)\n\n\nif __name__ == \"__main__\":\n main()\n","content_type":"text/x-python; charset=utf-8","language":"python","size":9742,"content_sha256":"eecae80d4fd42a114d829d562ca76c5ce73f60160e2f7e5d73a6dfb8e2ae98b2"},{"filename":"scripts/validate.py","content":"#!/usr/bin/env python3\n\"\"\"\n测试用例验证工具(v0.2)\n\n基于 docs/specs/测试用例文本协议.md 规范(v0.2版本)\n\n功能:\n1. 格式验证:检查用例是否符合文本协议 v0.2 格式\n2. 重复检测:基于标题和步骤相似度检测疑似重复用例\n\n用法:\n python validate.py test-case/模块/测试点.md # 验证单个文件\n python validate.py test-case/ # 验证整个目录\n python validate.py test-case/ --check-duplicates # 同时检测重复\n python validate.py test-case/ --duplicates-only # 仅检测重复\n\"\"\"\n\nimport sys\nimport argparse\nfrom pathlib import Path\nfrom typing import List, Tuple, Dict, Optional\nfrom dataclasses import dataclass\nfrom parse_text_protocol import TextProtocolParser, ParseError, TestCase\n\n\n@dataclass\nclass DuplicatePair:\n \"\"\"重复用例对\"\"\"\n case1: TestCase\n case2: TestCase\n file1: str\n file2: str\n similarity: float\n detail: Dict[str, float]\n\n\nclass ValidationResult:\n \"\"\"验证结果\"\"\"\n def __init__(self):\n self.total_files = 0\n self.total_cases = 0\n self.errors: List[Tuple[str, ParseError]] = []\n self.warnings: List[Tuple[str, str]] = []\n self.duplicates: List[DuplicatePair] = []\n\n def add_error(self, file_path: str, error: ParseError):\n self.errors.append((file_path, error))\n\n def add_warning(self, file_path: str, message: str):\n self.warnings.append((file_path, message))\n\n def add_duplicate(self, dup: DuplicatePair):\n self.duplicates.append(dup)\n\n def is_success(self) -> bool:\n return len(self.errors) == 0\n\n def print_summary(self, show_duplicates: bool = True):\n print(f\"\\n{'='*60}\")\n print(f\"验证完成\")\n print(f\"{'='*60}\")\n print(f\"文件数: {self.total_files}\")\n print(f\"用例数: {self.total_cases}\")\n print(f\"错误数: {len(self.errors)}\")\n print(f\"警告数: {len(self.warnings)}\")\n if show_duplicates:\n print(f\"疑似重复: {len(self.duplicates)} 对\")\n\n if self.errors:\n print(f\"\\n❌ 发现 {len(self.errors)} 个错误:\")\n for file_path, error in self.errors:\n print(f\" {file_path}:{error.line_number} {error.message}\")\n\n if self.warnings:\n print(f\"\\n⚠️ 发现 {len(self.warnings)} 个警告:\")\n for file_path, message in self.warnings:\n print(f\" {file_path}: {message}\")\n\n if show_duplicates and self.duplicates:\n print(f\"\\n🔄 发现 {len(self.duplicates)} 对疑似重复:\")\n for idx, dup in enumerate(self.duplicates, 1):\n print(f\"\\n [{idx}] 相似度: {dup.similarity:.1%}\")\n print(f\" 用例1: {dup.case1.title}\")\n print(f\" 文件: {dup.file1}\")\n print(f\" 用例2: {dup.case2.title}\")\n print(f\" 文件: {dup.file2}\")\n print(f\" 细节: 标题 {dup.detail['title']:.1%} | \"\n f\"步骤 {dup.detail['steps']:.1%} | \"\n f\"预期 {dup.detail['expected']:.1%}\")\n\n if self.is_success() and not self.duplicates:\n print(f\"\\n✅ 所有用例验证通过,无重复\")\n elif self.is_success():\n print(f\"\\n✅ 格式验证通过,但存在疑似重复\")\n else:\n print(f\"\\n❌ 验证失败\")\n\n\n# ============ 相似度计算 ============\n\ndef levenshtein_distance(s1: str, s2: str) -> int:\n \"\"\"编辑距离\"\"\"\n if len(s1) \u003c len(s2):\n return levenshtein_distance(s2, s1)\n if len(s2) == 0:\n return len(s1)\n previous_row = range(len(s2) + 1)\n for i, c1 in enumerate(s1):\n current_row = [i + 1]\n for j, c2 in enumerate(s2):\n insertions = previous_row[j + 1] + 1\n deletions = current_row[j] + 1\n substitutions = previous_row[j] + (c1 != c2)\n current_row.append(min(insertions, deletions, substitutions))\n previous_row = current_row\n return previous_row[-1]\n\n\ndef string_similarity(s1: str, s2: str) -> float:\n \"\"\"字符串相似度(基于编辑距离)\"\"\"\n if not s1 and not s2:\n return 1.0\n max_len = max(len(s1), len(s2))\n if max_len == 0:\n return 1.0\n return 1 - (levenshtein_distance(s1, s2) / max_len)\n\n\ndef jaccard_similarity(set1: set, set2: set) -> float:\n \"\"\"Jaccard 相似度\"\"\"\n if not set1 and not set2:\n return 1.0\n union = set1 | set2\n return len(set1 & set2) / len(union) if union else 0.0\n\n\ndef steps_to_set(steps: List[str]) -> set:\n \"\"\"将步骤列表转换为集合(用于 Jaccard 计算)\"\"\"\n return set(step.strip().lower() for step in steps if step.strip())\n\n\ndef calculate_similarity(case1: TestCase, case2: TestCase) -> Tuple[float, Dict[str, float]]:\n \"\"\"\n 计算两个用例的相似度\n \n Returns:\n (综合相似度, {title, steps, expected})\n \"\"\"\n # 标题相似度\n title_sim = string_similarity(case1.title, case2.title)\n \n # 步骤相似度(Jaccard)\n steps_sim = jaccard_similarity(steps_to_set(case1.steps), steps_to_set(case2.steps))\n \n # 预期结果相似度(Jaccard)\n expected_sim = jaccard_similarity(steps_to_set(case1.expected), steps_to_set(case2.expected))\n \n # 加权综合(标题 40%,步骤 40%,预期 20%)\n overall = title_sim * 0.4 + steps_sim * 0.4 + expected_sim * 0.2\n \n return overall, {\n 'title': title_sim,\n 'steps': steps_sim,\n 'expected': expected_sim\n }\n\n\n# ============ 验证逻辑 ============\n\ndef validate_file(file_path: Path, result: ValidationResult) -> List[Tuple[TestCase, str]]:\n \"\"\"\n 验证单个文件\n \n Returns:\n [(用例, 文件路径), ...] 用于后续重复检测\n \"\"\"\n result.total_files += 1\n cases_with_path = []\n\n parser = TextProtocolParser(strict=False)\n\n try:\n cases = parser.parse_file(file_path)\n result.total_cases += len(cases)\n\n for error in parser.errors:\n result.add_error(str(file_path), error)\n\n for case in cases:\n cases_with_path.append((case, str(file_path)))\n\n except ParseError as e:\n result.add_error(str(file_path), e)\n except Exception as e:\n result.add_error(str(file_path), ParseError(f\"未知错误: {e}\"))\n\n return cases_with_path\n\n\ndef validate_directory(dir_path: Path, result: ValidationResult) -> List[Tuple[TestCase, str]]:\n \"\"\"验证目录下的所有测试用例文件\"\"\"\n all_cases = []\n\n md_files = []\n for item_dir in dir_path.iterdir():\n if not item_dir.is_dir() or item_dir.name.startswith('.'):\n continue\n for md_file in item_dir.glob('*.md'):\n if md_file.name not in ['plan.md', 'all_cases.md']:\n md_files.append(md_file)\n\n # 也检查根目录下的 all_cases.md\n all_cases_file = dir_path / 'all_cases.md'\n if all_cases_file.exists():\n md_files.append(all_cases_file)\n\n if not md_files:\n print(f\"⚠️ 目录 {dir_path} 中没有找到测试用例文件\")\n return all_cases\n\n print(f\"找到 {len(md_files)} 个测试用例文件\")\n\n for md_file in md_files:\n print(f\"验证: {md_file}\")\n cases = validate_file(md_file, result)\n all_cases.extend(cases)\n\n return all_cases\n\n\ndef detect_duplicates(cases_with_path: List[Tuple[TestCase, str]], \n threshold: float = 0.7) -> List[DuplicatePair]:\n \"\"\"检测重复用例\"\"\"\n duplicates = []\n \n for i in range(len(cases_with_path)):\n for j in range(i + 1, len(cases_with_path)):\n case1, file1 = cases_with_path[i]\n case2, file2 = cases_with_path[j]\n \n similarity, detail = calculate_similarity(case1, case2)\n \n if similarity >= threshold:\n duplicates.append(DuplicatePair(\n case1=case1,\n case2=case2,\n file1=file1,\n file2=file2,\n similarity=similarity,\n detail=detail\n ))\n \n return duplicates\n\n\ndef main():\n parser = argparse.ArgumentParser(description=\"验证测试用例格式并检测重复\")\n parser.add_argument(\"path\", help=\"要验证的文件或目录路径\")\n parser.add_argument(\"--check-duplicates\", \"-d\", action=\"store_true\",\n help=\"同时检测重复用例\")\n parser.add_argument(\"--duplicates-only\", action=\"store_true\",\n help=\"仅检测重复,跳过格式验证\")\n parser.add_argument(\"--threshold\", \"-t\", type=float, default=0.7,\n help=\"重复检测阈值 0-1 (默认 0.7)\")\n args = parser.parse_args()\n\n path = Path(args.path)\n if not path.exists():\n print(f\"❌ 路径不存在: {path}\")\n sys.exit(1)\n\n result = ValidationResult()\n all_cases = []\n\n # 收集用例\n if path.is_file():\n all_cases = validate_file(path, result)\n elif path.is_dir():\n all_cases = validate_directory(path, result)\n else:\n print(f\"❌ 无效的路径: {path}\")\n sys.exit(1)\n\n # 重复检测\n check_dups = args.check_duplicates or args.duplicates_only\n if check_dups and all_cases:\n print(f\"\\n正在检测重复 (阈值 {args.threshold})...\")\n duplicates = detect_duplicates(all_cases, args.threshold)\n for dup in duplicates:\n result.add_duplicate(dup)\n\n # 输出结果\n result.print_summary(show_duplicates=check_dups)\n \n # 退出码\n if args.duplicates_only:\n sys.exit(1 if result.duplicates else 0)\n else:\n sys.exit(0 if result.is_success() else 1)\n\n\nif __name__ == \"__main__\":\n main()\n","content_type":"text/x-python; charset=utf-8","language":"python","size":9893,"content_sha256":"af914fa02cc8290a20faae684f4f4048193a71502d9bf77e868788907ba762bf"}],"content_json":{"type":"doc","content":[{"type":"heading","attrs":{"level":1},"content":[{"text":"测试用例生成器(testcase-generator)","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"1. 目标","type":"text"}]},{"type":"paragraph","content":[{"text":"根据测试点生成结构化测试用例,覆盖正向、反向、边界场景。","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"2. 输入输出","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"输入","type":"text","marks":[{"type":"strong"}]},{"text":":","type":"text"},{"text":"test-case/plan.md","type":"text","marks":[{"type":"code_inline"}]},{"text":"、","type":"text"},{"text":"clarified-requirements/index.md","type":"text","marks":[{"type":"code_inline"}]},{"text":"、","type":"text"},{"text":"CLAUDE.md","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"输出","type":"text","marks":[{"type":"strong"}]},{"text":":","type":"text"},{"text":"test-case/{ITEM}/{POINT}.md","type":"text","marks":[{"type":"code_inline"}]},{"text":"、","type":"text"},{"text":"test-case/all_cases.md","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"3. 核心原则","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"策略指导","type":"text","marks":[{"type":"strong"}]},{"text":":提供判断标准,而非强制数量","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"场景驱动","type":"text","marks":[{"type":"strong"}]},{"text":":基于实际场景复杂度生成用例","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"数据具体","type":"text","marks":[{"type":"strong"}]},{"text":":测试数据必须具体,不用占位符","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"可验证性","type":"text","marks":[{"type":"strong"}]},{"text":":预期结果必须明确可验证","type":"text"}]}]}]},{"type":"hr","attrs":{"markup":"---"}},{"type":"heading","attrs":{"level":2},"content":[{"text":"触发条件","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"用户执行 ","type":"text"},{"text":"/testcase-gen","type":"text","marks":[{"type":"code_inline"}]},{"text":" 命令","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"test-case/plan.md","type":"text","marks":[{"type":"code_inline"}]},{"text":" 存在","type":"text"}]}]}]},{"type":"hr","attrs":{"markup":"---"}},{"type":"heading","attrs":{"level":2},"content":[{"text":"4. 核心理论","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"4.1 等价类划分法","type":"text"}]},{"type":"paragraph","content":[{"text":"将输入数据划分为若干等价类,从每类中选取代表性数据测试。","type":"text"}]},{"type":"paragraph","content":[{"text":"应用示例","type":"text","marks":[{"type":"strong"}]},{"text":":","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":""},"content":[{"text":"POINT: 用户名密码登录\n\n输入项: 用户名、密码\n\n用户名等价类:\n 有效: [6-20字符,字母数字下划线] → \"test_user123\"\n 无效: [\u003c6] \"ab\" | [>20] \"verylongusername12345\" | [特殊字符] \"user@name\" | [空] \"\"\n\n密码等价类:\n 有效: [8-20字符,含大小写+数字] → \"Test1234\"\n 无效: [\u003c8] \"Test12\" | [缺大写] \"test1234\" | [空] \"\"\n\n生成原则: 一次测试一个等价类,其他保持有效","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"4.2 边界值分析法","type":"text"}]},{"type":"paragraph","content":[{"text":"边界是最容易出错的地方。","type":"text"}]},{"type":"paragraph","content":[{"text":"边界三点法","type":"text","marks":[{"type":"strong"}]},{"text":": 离点(min-1) | 上点(min) | 内点(中间值) | 上点(max) | 离点(max+1)","type":"text"}]},{"type":"paragraph","content":[{"text":"应用示例","type":"text","marks":[{"type":"strong"}]},{"text":":","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":""},"content":[{"text":"用户名长度 6-20 字符:\n- 离点-下: 5字符 \"abcde\" → 失败\n- 上点-下: 6字符 \"abcdef\" → 成功\n- 内点: 13字符 \"test_user_123\" → 成功\n- 上点-上: 20字符 \"test_user_1234567890\" → 成功\n- 离点-上: 21字符 \"test_user_12345678901\" → 失败","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"4.3 用例数量判断","type":"text"}]},{"type":"paragraph","content":[{"text":"不强制数量,根据场景复杂度自适应调整。","type":"text"}]},{"type":"paragraph","content":[{"text":"判断标准","type":"text","marks":[{"type":"strong"}]},{"text":":","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"输入项数量:输入项越多,用例越多","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"业务规则复杂度:规则越复杂,用例越多","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"风险等级:Critical 和 High 需要更多用例","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"测试关注点:关注点越多,用例越多","type":"text"}]}]}]},{"type":"hr","attrs":{"markup":"---"}},{"type":"heading","attrs":{"level":2},"content":[{"text":"5. 用例格式规范","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"5.1 Markdown文本协议格式 (v0.2)","type":"text"}]},{"type":"paragraph","content":[{"text":"每个测试点生成一个 ","type":"text"},{"text":".md","type":"text","marks":[{"type":"code_inline"}]},{"text":" 文件,格式如下:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"markdown"},"content":[{"text":"## [P1] 用例标题\n[测试类型] 功能\n[前置条件] 前置条件描述\n[测试步骤] 1. 步骤1。2. 步骤2\n[预期结果] 1. 预期1。2. 预期2\n\n## [P3][反向] 用例标题\n[测试类型] 功能\n[前置条件] 前置条件描述\n[测试步骤] 1. 步骤1。2. 步骤2\n[预期结果] 1. 预期1。2. 预期2","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"5.2 字段说明","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"优先级","type":"text","marks":[{"type":"strong"}]},{"text":":P1-P5(必填)","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"P1: 核心功能正向流程","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"P2: 基本功能正向流程","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"P3: 核心功能异常场景","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"P4: 边界条件","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"P5: 低频场景","type":"text"}]}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"[反向]","type":"text","marks":[{"type":"strong"}]},{"text":":反向用例标记(可选)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"测试类型","type":"text","marks":[{"type":"strong"}]},{"text":":功能、接口、性能、安全、兼容、易用、安装部署、可靠、本地化、可维护、可扩展、配置(12选1)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"前置条件","type":"text","marks":[{"type":"strong"}]},{"text":":测试前的准备工作","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"测试步骤","type":"text","marks":[{"type":"strong"}]},{"text":":操作步骤,用句号分隔","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"预期结果","type":"text","marks":[{"type":"strong"}]},{"text":":期望的结果,与步骤一一对应","type":"text"}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"5.3 测试类型枚举(12种)","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":""},"content":[{"text":"功能\n兼容性\n易用性\n性能\n稳定性\n安全性\n可靠性\n效果(AI类、资源类)\n效果(硬件器件类)\n可维护性\n可移植性\n埋点","type":"text"}]},{"type":"paragraph","content":[{"text":"注意","type":"text","marks":[{"type":"strong"}]},{"text":":","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"不能写\"功能测试\",必须是\"功能\"","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"括号必须是中文括号","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"大小写敏感","type":"text"}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"5.4 步骤格式规范","type":"text"}]},{"type":"paragraph","content":[{"text":"推荐格式","type":"text","marks":[{"type":"strong"}]},{"text":": 使用中文句号分隔,编号连续","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"markdown"},"content":[{"text":"[测试步骤] 1. 打开登录页面。2. 输入用户名test_user。3. 输入密码Test1234。4. 点击登录按钮\n[预期结果] 1. 页面正常显示。2. 用户名输入框显示内容。3. 密码输入框显示密码。4. 登录成功,跳转到首页","type":"text"}]},{"type":"paragraph","content":[{"text":"重要","type":"text","marks":[{"type":"strong"}]},{"text":": 编号必须从1开始,连续递增;测试步骤和预期结果的编号数量必须一致。","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"5.5 测试数据要求","type":"text"}]},{"type":"paragraph","content":[{"text":"必须具体,不用占位符:","type":"text"}]},{"type":"paragraph","content":[{"text":"正确示例","type":"text","marks":[{"type":"strong"}]},{"text":":","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"用户名:test_user","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"密码:Test1234","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"手机号:13800138000","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"金额:99.99","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"错误示例","type":"text","marks":[{"type":"strong"}]},{"text":":","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"用户名:{valid_username}","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"密码:{password}","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"手机号:{phone}","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"金额:{amount}","type":"text"}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"5.6 预期结果要求","type":"text"}]},{"type":"paragraph","content":[{"text":"必须明确可验证:","type":"text"}]},{"type":"paragraph","content":[{"text":"正确示例","type":"text","marks":[{"type":"strong"}]},{"text":":","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"跳转到 /home 页面,顶部显示\"欢迎,test_user\"","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"提示\"密码长度不足,请输入8-20位密码\"","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"订单状态变为\"已支付\",库存减少1","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"错误示例","type":"text","marks":[{"type":"strong"}]},{"text":":","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"登录成功","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"提示错误信息","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"系统正常处理","type":"text"}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"5.7 目录结构","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":""},"content":[{"text":"test-case/\n├── plan.md\n├── {ITEM}/\n│ ├── {POINT1}.md\n│ ├── {POINT2}.md\n│ └── ...\n└── all_cases.md","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"5.8 合并规则","type":"text"}]},{"type":"paragraph","content":[{"text":"将所有 ","type":"text"},{"text":"{ITEM}/{POINT}.md","type":"text","marks":[{"type":"code_inline"}]},{"text":" 合并到 ","type":"text"},{"text":"all_cases.md","type":"text","marks":[{"type":"code_inline"}]},{"text":":","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"按 ITEM 分组","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"每个 ITEM 下按 POINT 排序","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"添加分隔标记:","type":"text"},{"text":"# ITEM 名称","type":"text","marks":[{"type":"code_inline"}]},{"text":" 和 ","type":"text"},{"text":"## POINT 名称","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"hr","attrs":{"markup":"---"}},{"type":"heading","attrs":{"level":2},"content":[{"text":"6. 用例生成流程","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Step 1: 准备上下文","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"cat test-case/plan.md\ncat clarified-requirements/index.md\ncat CLAUDE.md","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Step 2: 理解测试点","type":"text"}]},{"type":"paragraph","content":[{"text":"对每个 POINT:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"读取 POINT 名称、风险等级、测试关注点","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"读取需求文档中的相关描述","type":"text"}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Step 3: 识别输入项","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"列出所有输入项(字段、参数、操作等)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"识别输入项的约束条件","type":"text"}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Step 4: 划分等价类和边界值","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"有效等价类","type":"text","marks":[{"type":"strong"}]},{"text":":符合规则的输入","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"无效等价类","type":"text","marks":[{"type":"strong"}]},{"text":":违反规则的输入","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"边界值","type":"text","marks":[{"type":"strong"}]},{"text":":临界点(最小值、最大值、临界值)","type":"text"}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Step 5: 设计测试用例","type":"text"}]},{"type":"paragraph","content":[{"text":"基于以下策略设计:","type":"text"}]},{"type":"paragraph","content":[{"text":"核心思考框架","type":"text","marks":[{"type":"strong"}]},{"text":":","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"输入分析","type":"text","marks":[{"type":"strong"}]}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"该场景涉及哪些输入项?","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"每个输入有什么约束(长度、格式、范围)?","type":"text"}]}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"等价类识别","type":"text","marks":[{"type":"strong"}]}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"有效等价类:正常情况下的典型值","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"无效等价类:各种异常情况(空值、越界、格式错误)","type":"text"}]}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"测试价值评估","type":"text","marks":[{"type":"strong"}]}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"这个等价类失败时,会暴露不同的缺陷吗?","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"如果和其他等价类的测试逻辑相同,是否可以合并?","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"测试关注点中提到的场景,如何在用例中体现?","type":"text"}]}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"覆盖判断","type":"text","marks":[{"type":"strong"}]}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"有效流程至少有一个用例","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"核心异常场景已覆盖","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"边界条件根据实际情况选择","type":"text"}]}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"用例设计","type":"text","marks":[{"type":"strong"}]}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"核心正向用例(P1):最常见的成功场景","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"基本正向用例(P2):其他有效等价类","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"核心异常用例(P3):关键失败场景","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"边界条件用例(P4):边界值测试","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"低频场景用例(P5):不常见但需覆盖的场景","type":"text"}]}]}]}]}]},{"type":"paragraph","content":[{"text":"用例质量标准","type":"text","marks":[{"type":"strong"}]},{"text":":","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"数据具体(\"test123\",不要\"正确用户名\")","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"预期结果可验证(\"显示错误提示'用户名不能为空'\",不要\"提示错误\")","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"命名规范(以\"验证\"开头)","type":"text"}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Step 6: 生成完毕后统一校验","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"每个测试项(ITEM)生成完毕后,调用校验脚本检查格式","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"全部测试项生成完毕后,再次调用校验脚本进行整体检查","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"校验脚本路径:","type":"text"},{"text":".claude/skills/testcase-generator/scripts/validate.py","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"hr","attrs":{"markup":"---"}},{"type":"heading","attrs":{"level":2},"content":[{"text":"7. 示例","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"示例1:正确的用例(用户名密码登录)","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"markdown"},"content":[{"text":"## [P1] 验证有效用户名和密码登录成功\n[测试类型] 功能\n[前置条件] 已注册用户test_user,密码Test1234\n[测试步骤] 1. 输入用户名test_user,输入密码Test1234,点击登录。2. 验证跳转到首页并显示用户名\n[预期结果] 1. 登录请求成功。2. 跳转到/home页面,顶部显示'欢迎,test_user'\n\n## [P3][反向] 验证用户名为空时登录失败\n[测试类型] 功能\n[前置条件] 进入登录页\n[测试步骤] 1. 不输入用户名,输入密码Test1234,点击登录。2. 验证提示'用户名不能为空'\n[预期结果] 1. 登录请求被拒绝。2. 停留在登录页,用户名输入框下方显示红色提示'用户名不能为空'\n\n## [P4] 验证密码长度边界值(8位)\n[测试类型] 功能\n[前置条件] 已注册用户test_user,密码Test1234\n[测试步骤] 1. 输入用户名test_user,输入8位密码Test1234,点击登录。2. 验证登录成功\n[预期结果] 1. 登录请求成功。2. 跳转到/home页面","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"示例2:综合场景(多输入项)","type":"text"}]},{"type":"paragraph","content":[{"text":"POINT","type":"text","marks":[{"type":"strong"}]},{"text":": 广告账户列表-筛选功能","type":"text"}]},{"type":"paragraph","content":[{"text":"输入项分析","type":"text","marks":[{"type":"strong"}]},{"text":":","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"时间范围: [今天|昨天|近7天|自定义]","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"账户名称: [文本模糊搜索]","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"账户状态: [正常|封禁|审核中]","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"生成用例(6个)","type":"text","marks":[{"type":"strong"}]},{"text":":","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"markdown"},"content":[{"text":"## [P1] 验证单一条件筛选成功\n[测试类型] 功能\n[前置条件] 广告账户列表页面,有10个账户\n[测试步骤] 1. 选择时间\"近7天\",点击查询。2. 验证返回符合条件的账户\n[预期结果] 1. 列表刷新。2. 仅显示近7天有数据的账户\n\n## [P1] 验证多条件组合筛选成功\n[测试类型] 功能\n[前置条件] 同上\n[测试步骤] 1. 选择时间\"近7天\",输入名称\"test\",选择状态\"正常\",点击查询\n[预期结果] 1. 列表刷新。2. 仅显示同时满足3个条件的账户\n\n## [P3][反向] 验证时间超过365天筛选失败\n[测试类型] 功能\n[前置条件] 同上\n[测试步骤] 1. 选择自定义时间,开始日期2023-01-01,结束日期2024-01-02,点击查询\n[预期结果] 1. 显示提示\"时间范围不能超过365天\",列表不刷新\n\n## [P3][反向] 验证不存在的账户名筛选返回空\n[测试类型] 功能\n[前置条件] 同上\n[测试步骤] 1. 输入不存在的账户名\"nonexistent123\",点击查询\n[预期结果] 1. 列表刷新。2. 显示\"暂无数据\"\n\n## [P2] 验证筛选条件清空功能\n[测试类型] 功能\n[前置条件] 已应用筛选条件\n[测试步骤] 1. 点击\"重置\"按钮。2. 验证所有筛选条件清空,列表恢复默认\n[预期结果] 1. 所有筛选项恢复默认值。2. 列表显示全部账户\n\n## [P4][反向] 验证结束时间早于开始时间提示错误\n[测试类型] 功能\n[前置条件] 同上\n[测试步骤] 1. 选择自定义时间,开始日期2024-01-02,结束日期2024-01-01,点击查询\n[预期结果] 1. 显示提示\"结束时间不能早于开始时间\"","type":"text"}]},{"type":"paragraph","content":[{"text":"说明","type":"text","marks":[{"type":"strong"}]},{"text":":","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"中等复杂度 POINT(多输入项)生成 6 个用例","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"覆盖:有效组合、无效边界、清空操作","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"选择","type":"text"},{"text":"代表性用例","type":"text","marks":[{"type":"strong"}]},{"text":",覆盖主要场景","type":"text"}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"示例3:错误的用例","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"markdown"},"content":[{"text":"## [P1] 验证登录成功\n[测试类型] 功能\n[前置条件] 用户已注册\n[测试步骤] 1. 输入用户名{username},输入密码{password},点击登录\n[预期结果] 1. 登录成功","type":"text"}]},{"type":"paragraph","content":[{"text":"为什么错误","type":"text","marks":[{"type":"strong"}]},{"text":":","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"测试数据使用占位符({username}、{password})","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"预期结果不可验证(\"登录成功\"太模糊)","type":"text"}]}]}]},{"type":"hr","attrs":{"markup":"---"}},{"type":"heading","attrs":{"level":2},"content":[{"text":"8. 检验流程","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Step 1: 每个测试项生成完毕后","type":"text"}]},{"type":"paragraph","content":[{"text":"调用校验脚本检查格式:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"uv run .claude/skills/testcase-generator/scripts/validate.py \\\n --single \"test-case/{模块}/{测试点}.md\"","type":"text"}]},{"type":"paragraph","content":[{"text":"脚本会输出日志,指出格式问题(如优先级错误、字段缺失等)。AI 分析日志,决定是否需要修改。","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Step 2: 全部生成完毕后","type":"text"}]},{"type":"paragraph","content":[{"text":"再次调用校验脚本进行整体检查:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"uv run .claude/skills/testcase-generator/scripts/validate.py \"test-case/\" --check-duplicates","type":"text"}]},{"type":"paragraph","content":[{"text":"脚本会检查重复用例、格式一致性等。脚本只提供日志,不直接修改文件。AI 分析日志,决定是否需要调整。","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Step 3: 生成质量报告","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"统计用例数量(按 ITEM、按优先级)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"统计等价类覆盖率","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"统计边界值覆盖率","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"列出重复用例(如果有)","type":"text"}]}]}]},{"type":"hr","attrs":{"markup":"---"}},{"type":"heading","attrs":{"level":2},"content":[{"text":"9. 检查清单","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"生成前检查","type":"text"}]},{"type":"checkbox_list","attrs":{"id":null},"content":[{"type":"checkbox_item","attrs":{"checked":false},"content":[{"type":"paragraph","content":[{"text":"已读取 ","type":"text"},{"text":"test-case/plan.md","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"checkbox_item","attrs":{"checked":false},"content":[{"type":"paragraph","content":[{"text":"已读取 ","type":"text"},{"text":"clarified-requirements/index.md","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"checkbox_item","attrs":{"checked":false},"content":[{"type":"paragraph","content":[{"text":"已读取 ","type":"text"},{"text":"CLAUDE.md","type":"text","marks":[{"type":"code_inline"}]},{"text":" 业务背景","type":"text"}]}]},{"type":"checkbox_item","attrs":{"checked":false},"content":[{"type":"paragraph","content":[{"text":"理解测试项(ITEM)和测试点(POINT)的层级关系","type":"text"}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"每个POINT生成时检查","type":"text"}]},{"type":"checkbox_list","attrs":{"id":null},"content":[{"type":"checkbox_item","attrs":{"checked":false},"content":[{"type":"paragraph","content":[{"text":"每个 POINT 都已生成用例文件","type":"text"}]}]},{"type":"checkbox_item","attrs":{"checked":false},"content":[{"type":"paragraph","content":[{"text":"用例数量合理(基于场景复杂度)","type":"text"}]}]},{"type":"checkbox_item","attrs":{"checked":false},"content":[{"type":"paragraph","content":[{"text":"所有用例都有优先级(P1-P5)","type":"text"}]}]},{"type":"checkbox_item","attrs":{"checked":false},"content":[{"type":"paragraph","content":[{"text":"所有用例都有测试类型(12种之一)","type":"text"}]}]},{"type":"checkbox_item","attrs":{"checked":false},"content":[{"type":"paragraph","content":[{"text":"测试数据具体(无占位符)","type":"text"}]}]},{"type":"checkbox_item","attrs":{"checked":false},"content":[{"type":"paragraph","content":[{"text":"预期结果可验证(无模糊描述)","type":"text"}]}]},{"type":"checkbox_item","attrs":{"checked":false},"content":[{"type":"paragraph","content":[{"text":"测试步骤与预期结果数量一致","type":"text"}]}]},{"type":"checkbox_item","attrs":{"checked":false},"content":[{"type":"paragraph","content":[{"text":"用例标题以\"验证\"开头","type":"text"}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"治理阶段检查","type":"text"}]},{"type":"checkbox_list","attrs":{"id":null},"content":[{"type":"checkbox_item","attrs":{"checked":false},"content":[{"type":"paragraph","content":[{"text":"执行了全局校验脚本","type":"text"}]}]},{"type":"checkbox_item","attrs":{"checked":false},"content":[{"type":"paragraph","content":[{"text":"检查了重复用例","type":"text"}]}]},{"type":"checkbox_item","attrs":{"checked":false},"content":[{"type":"paragraph","content":[{"text":"格式符合规范(已通过 validate.py 检查)","type":"text"}]}]},{"type":"checkbox_item","attrs":{"checked":false},"content":[{"type":"paragraph","content":[{"text":"已生成 all_cases.md","type":"text"}]}]},{"type":"checkbox_item","attrs":{"checked":false},"content":[{"type":"paragraph","content":[{"text":"无重复用例(已通过 validate.py --check-duplicates 检查)","type":"text"}]}]},{"type":"checkbox_item","attrs":{"checked":false},"content":[{"type":"paragraph","content":[{"text":"已生成质量报告","type":"text"}]}]}]},{"type":"hr","attrs":{"markup":"---"}},{"type":"heading","attrs":{"level":2},"content":[{"text":"10. 脚本接口","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"validate.py","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"# 单文件校验\nuv run .claude/skills/testcase-generator/scripts/validate.py \\\n --single \"test-case/{模块}/{测试点}.md\"\n\n# 全局校验\nuv run .claude/skills/testcase-generator/scripts/validate.py \"test-case/\"\n\n# 全局校验 + 重复检测\nuv run .claude/skills/testcase-generator/scripts/validate.py \"test-case/\" --check-duplicates\n\n# 仅重复检测\nuv run .claude/skills/testcase-generator/scripts/validate.py \"test-case/\" --duplicates-only","type":"text"}]},{"type":"paragraph","content":[{"text":"校验项","type":"text","marks":[{"type":"strong"}]},{"text":":","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Schema格式","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"必填字段(优先级、测试类型、测试步骤、预期结果)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"测试类型枚举(12种)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"步骤编号连续性","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"步骤与预期结果数量一致性","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"等价类覆盖检查(","type":"text"},{"text":"--check-equivalence","type":"text","marks":[{"type":"code_inline"}]},{"text":")","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"边界值覆盖检查(","type":"text"},{"text":"--check-boundary","type":"text","marks":[{"type":"code_inline"}]},{"text":")","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"重复检测(","type":"text"},{"text":"--check-duplicates","type":"text","marks":[{"type":"code_inline"}]},{"text":")","type":"text"}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"to_excel.py","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"# 从目录导出\nuv run .claude/skills/testcase-generator/scripts/to_excel.py \"test-case/\" -o \"test-case/export.xlsx\"\n\n# 从单个文件导出\nuv run .claude/skills/testcase-generator/scripts/to_excel.py \"test-case/{模块}/{测试点}.md\" -o \"output.xlsx\"","type":"text"}]},{"type":"hr","attrs":{"markup":"---"}},{"type":"heading","attrs":{"level":2},"content":[{"text":"11. 异常处理","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"错误","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"处理方式","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"单文件校验失败","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"重试3次,仍失败则跳过并记录","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"用例数过多(>15)","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"提示检查是否有冗余等价类","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"缺少边界用例","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"警告并建议补充","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"缺少无效等价类","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"警告并建议补充异常场景","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"步骤编号不连续","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"报错并要求修正","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"测试类型不在枚举","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"报错并要求修正","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"步骤与预期数量不一致","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"报错并要求修正","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"导出失败","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"检查openpyxl依赖","type":"text"}]}]}]}]},{"type":"hr","attrs":{"markup":"---"}},{"type":"heading","attrs":{"level":2},"content":[{"text":"12. 停止点","type":"text"}]},{"type":"paragraph","content":[{"text":"✅ ","type":"text"},{"text":"用例生成完成","type":"text","marks":[{"type":"strong"}]}]},{"type":"paragraph","content":[{"text":"产物","type":"text","marks":[{"type":"strong"}]},{"text":":","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"test-case/{ITEM}/{POINT}.md","type":"text","marks":[{"type":"code_inline"}]},{"text":" - 各测试点的用例文件","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"test-case/all_cases.md","type":"text","marks":[{"type":"code_inline"}]},{"text":" - 所有用例汇总","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"质量保证","type":"text","marks":[{"type":"strong"}]},{"text":":","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"基于等价类理论生成","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"边界值全覆盖","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"用例数量合理(基于场景复杂度)","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"统计示例","type":"text","marks":[{"type":"strong"}]},{"text":":","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":""},"content":[{"text":"📊 生成统计:\n - 测试项: 8个\n - 测试点: 20个\n - 测试用例: 75个\n\n 覆盖率:\n - 有效等价类: 100% (每POINT至少1个)\n - 无效等价类: 80% (主要异常全覆盖)\n - 边界值: 90% (所有范围型输入)\n\n 用例分布:\n - P1(核心正向): 20个(27%)\n - P2(基本正向): 15个(20%)\n - P3(核心异常): 25个(33%)\n - P4(边界条件): 12个(16%)\n - P5(低频场景): 3个(4%)","type":"text"}]},{"type":"paragraph","content":[{"text":"下一步","type":"text","marks":[{"type":"strong"}]},{"text":":","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"人工审核测试用例","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"导出为 Excel 格式(可选)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"根据业务理解调整用例","type":"text"}]}]}]},{"type":"hr","attrs":{"markup":"---"}}]},"metadata":{"date":"2026-06-05","name":"testcase-generator","author":"@skillopedia","source":{"stars":10,"repo_name":"twu","origin_url":"https://github.com/chyax98/twu/blob/HEAD/.claude/skills/testcase-generator/SKILL.md","repo_owner":"chyax98","body_sha256":"53e6cf53d62478eeb78da0bf558f10d7accebf9b7f6d3f39922f5b5749e50c42","cluster_key":"655853a035bc39e74f7402f5f7015f3dba54cbdb577313b76b79ca6a738b595d","clean_bundle":{"format":"clean-skill-bundle-v1","source":"chyax98/twu/.claude/skills/testcase-generator/SKILL.md","attachments":[{"id":"f95f4a77-9ff3-5dc8-9776-e39832b6e501","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/f95f4a77-9ff3-5dc8-9776-e39832b6e501/attachment.md","path":"references/PROMPTS.md","size":5381,"sha256":"86512f17d61248bcf9194d26c9872af5fa34a52460486d9b7c70c3004d0a7020","contentType":"text/markdown; charset=utf-8"},{"id":"d5f73adc-da2e-535a-98f2-afdd6f2ee476","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/d5f73adc-da2e-535a-98f2-afdd6f2ee476/attachment.py","path":"scripts/parse_text_protocol.py","size":14026,"sha256":"aa3eb520a45842a72eff4acf7d04c46341e2b2c92849a4cb6403c5cdc20bd4b4","contentType":"text/x-python; charset=utf-8"},{"id":"edcbd819-5b3b-513b-9be5-87ed532e3710","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/edcbd819-5b3b-513b-9be5-87ed532e3710/attachment.py","path":"scripts/to_excel.py","size":9742,"sha256":"eecae80d4fd42a114d829d562ca76c5ce73f60160e2f7e5d73a6dfb8e2ae98b2","contentType":"text/x-python; charset=utf-8"},{"id":"c1c702f0-9c4c-5752-a837-9ba4f0c0f212","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/c1c702f0-9c4c-5752-a837-9ba4f0c0f212/attachment.py","path":"scripts/validate.py","size":9893,"sha256":"af914fa02cc8290a20faae684f4f4048193a71502d9bf77e868788907ba762bf","contentType":"text/x-python; charset=utf-8"}],"bundle_sha256":"734ee6aec147ccd9d83ba9710914c64b2ba098bfa27dfa49e73fa6110d4e7f0e","attachment_count":4,"text_attachments":4,"attachment_storage":"skillopedia-attachments-v1","binary_attachments":0,"excluded_attachments":[]},"cluster_size":1,"skill_md_path":".claude/skills/testcase-generator/SKILL.md","import_metadata":{"date":"2026-06-05","author":"@skillopedia","version":"v1","category":"testing-qa","category_label":"Testing"},"exact_dupes_collapsed_into_this":0},"version":"v1","category":"testing-qa","import_tag":"clean-skills-v1","description":"测试用例生成器 - 基于等价类划分和边界值分析理论,按测试点(POINT)分批生成高质量测试用例,输出为 Markdown 格式。当用户执行 /testcase-gen 命令或需要生成测试用例时使用。","allowed-tools":"Read, Write, Bash, Glob"}},"renderedAt":1782979664949}

测试用例生成器(testcase-generator) 1. 目标 根据测试点生成结构化测试用例,覆盖正向、反向、边界场景。 2. 输入输出 - 输入 : 、 、 - 输出 : 、 3. 核心原则 1. 策略指导 :提供判断标准,而非强制数量 2. 场景驱动 :基于实际场景复杂度生成用例 3. 数据具体 :测试数据必须具体,不用占位符 4. 可验证性 :预期结果必须明确可验证 --- 触发条件 - 用户执行 命令 - 存在 --- 4. 核心理论 4.1 等价类划分法 将输入数据划分为若干等价类,从每类中选取代表性数据测试。 应用示例 : 4.2 边界值分析法 边界是最容易出错的地方。 边界三点法 : 离点(min-1) | 上点(min) | 内点(中间值) | 上点(max) | 离点(max+1) 应用示例 : 4.3 用例数量判断 不强制数量,根据场景复杂度自适应调整。 判断标准 : - 输入项数量:输入项越多,用例越多 - 业务规则复杂度:规则越复杂,用例越多 - 风险等级:Critical 和 High 需要更多用例 - 测试关注点:关注点越多,用例越多 --- 5. 用例格式规范 5.1 Markdown文本协议格式 (v0.2) 每个测试点生成一个 文件,格式如下: 5.2 字段说明 - 优先级 :P1-P5(必填) - P1: 核心功能正向流程 - P2: 基本…