skills/check-test-code-quality/rules/R008/SKILL.md
# R008: 用例声明格式不规范 ## 规则信息 | 属性 | 值 | |------|------| | 规则编号 | R008 | | 问题类型 | 用例声明格式不规范 | | 严重级别 | Warning | | 规则复杂度 | complex | ## 问题描述 测试用例的文档注释格式不符合规范要求,包括缺少注释标记、分隔符错误、空行等问题。 **规范来源**: 用例低级问题.md 第15条 — "XTS上库用例声明需符合要求" ### 规范要求 1. 文档注释以 `/**` 开头,以 `*/` 结尾,每行以 `*` 开始 2. 参数名以 `@` 修饰,参数名和参数值以(一或若干个)空格分隔,禁止使用其他分隔符 3. 文档注释结束行的下一行应紧接要修饰的测试用例,禁止出现空行 ## 扫描范围 | 应扫描 | 文件扩展名 | |--------|-----------| | **测试文件** | `.test.ets`, `.test.ts`, `.test.js` | ## 检测逻辑 ### 步骤1: 收集测试文件 ```python def get_
npx skillsauth add openharmonyinsight/openharmony-skills skills/check-test-code-quality/rules/R008Install this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
| 属性 | 值 | |------|------| | 规则编号 | R008 | | 问题类型 | 用例声明格式不规范 | | 严重级别 | Warning | | 规则复杂度 | complex |
测试用例的文档注释格式不符合规范要求,包括缺少注释标记、分隔符错误、空行等问题。
规范来源: 用例低级问题.md 第15条 — "XTS上库用例声明需符合要求"
/** 开头,以 */ 结尾,每行以 * 开始@ 修饰,参数名和参数值以(一或若干个)空格分隔,禁止使用其他分隔符| 应扫描 | 文件扩展名 |
|--------|-----------|
| 测试文件 | .test.ets, .test.ts, .test.js |
def get_r008_scan_files(directory: str) -> list[str]:
test_files = get_test_files(directory) # .test.ets, .test.ts, .test.js
return test_files
在测试文件中,找到所有位于 it( 调用之前的文档注释块:
import re
def extract_doc_blocks(content: str) -> list[dict]:
lines = content.split('\n')
doc_blocks = []
i = 0
while i < len(lines):
line = lines[i]
stripped = line.strip()
# 查找 /** 开头的文档注释
if stripped.startswith('/**'):
block_start = i + 1 # 1-indexed
block_lines = [line]
j = i + 1
# 收集注释块内容直到 */
while j < len(lines):
block_lines.append(lines[j])
if '*/' in lines[j]:
break
j += 1
block_end = j + 1 # 1-indexed
block_text = '\n'.join(block_lines)
# 检查注释块后是否紧跟 it() 调用
next_code_line_idx = j + 1
testcase_name = None
has_empty_line = False
# 跳过空行
while next_code_line_idx < len(lines) and lines[next_code_line_idx].strip() == '':
has_empty_line = True
next_code_line_idx += 1
if next_code_line_idx < len(lines):
next_line = lines[next_code_line_idx].strip()
tc_match = re.search(r"\bit\s*\(\s*['\"]([^'\"]+)['\"]", next_line)
if tc_match:
testcase_name = tc_match.group(1)
doc_blocks.append({
'start_line': block_start,
'end_line': block_end,
'lines': block_lines,
'text': block_text,
'testcase': testcase_name or '-',
'has_empty_line_before_it': has_empty_line,
'next_code_line': next_code_line_idx + 1 if next_code_line_idx < len(lines) else None
})
i = j + 1
else:
i += 1
return doc_blocks
对每个文档注释块,检测以下5种问题:
def check_doc_block(block: dict) -> list[dict]:
issues = []
lines = block['lines']
testcase = block['testcase']
# 问题1: 缺少 /** 开头或 */ 结尾
first_line = lines[0].strip()
last_line = lines[-1].strip()
if not first_line.startswith('/**'):
issues.append({
'line': block['start_line'],
'type': 'missing_opening',
'snippet': first_line,
'detail': '文档注释未以/**开头'
})
if '*/' not in last_line and (len(lines) == 1 or '*/' not in lines[-1]):
issues.append({
'line': block['end_line'],
'type': 'missing_closing',
'snippet': last_line,
'detail': '文档注释未以*/结尾'
})
# 问题2: 每行未以 * 开始(跳过首行和末行)
for idx, line in enumerate(lines[1:-1], start=block['start_line'] + 1):
stripped = line.strip()
if stripped and not stripped.startswith('*'):
issues.append({
'line': idx,
'type': 'missing_asterisk',
'snippet': stripped,
'detail': '注释行未以*开始'
})
# 问题3: 参数名缺少 @ 修饰符
for idx, line in enumerate(lines, start=block['start_line']):
stripped = line.strip().lstrip('*').strip()
# 匹配 tc.name, tc.number 等无 @ 前缀的参数
no_at_match = re.match(r'^(tc\.\w+)\s+\S', stripped)
if no_at_match:
issues.append({
'line': idx,
'type': 'missing_at_modifier',
'snippet': line.strip(),
'detail': f'参数 {no_at_match.group(1)} 缺少@修饰符'
})
# 问题4: 使用冒号分隔符(应使用空格)
for idx, line in enumerate(lines, start=block['start_line']):
# 匹配 @tc.name : value 或 @tc.name: value 模式
colon_match = re.search(r'@(tc\.\w+)\s*:\s', line)
if colon_match:
issues.append({
'line': idx,
'type': 'colon_separator',
'snippet': line.strip(),
'detail': f'参数 {colon_match.group(1)} 使用了冒号分隔符,应使用空格'
})
# 问题5: 注释结束行与测试用例之间存在空行
if block['has_empty_line_before_it'] and block['testcase'] != '-':
issues.append({
'line': block['end_line'],
'type': 'empty_line_before_it',
'snippet': '*/ 后存在空行',
'detail': '文档注释结束行与测试用例之间不应有空行'
})
return issues
def check_r008(file_path: str, content: str) -> list[dict]:
doc_blocks = extract_doc_blocks(content)
all_issues = []
for block in doc_blocks:
issues = check_doc_block(block)
for issue in issues:
all_issues.append({
'rule': 'R008',
'type': '用例声明格式不规范',
'severity': 'Warning',
'file': file_path,
'line': issue['line'],
'testcase': block['testcase'],
'snippet': issue['snippet'],
'suggestion': (
f'路径: {file_path}, 行号: {issue["line"]}, '
f'问题描述: {issue["detail"]}'
)
})
return all_issues
| 列名 | 说明 |
|------|------|
| 问题ID | R008 |
| 问题类型 | 用例声明格式不规范 |
| 严重级别 | Warning |
| 文件路径 | 相对路径 |
| 行号 | 问题所在行号 |
| 所属用例 | 关联的 it(' 参数名 |
| 代码片段 | 匹配到的代码行 |
| 修复建议 | 路径+行号+问题描述 |
// 错误1: 缺少@修饰符
/**
* tc.name testName // ✗ 错误:缺少@
* tc.number SUB_XXX_XXXX_XXXX // ✗ 错误:缺少@
*/
it('testName', () => {
console.info('test001');
});
// 错误2: 使用/* 开头(应为/**)
/* // ✗ 错误:应使用 /**
* @tc.name testName
*/
it('testName', () => {
console.info('test001');
});
// 错误3: 使用冒号分隔
/**
* @tc.name : testName // ✗ 错误:不应使用冒号
* @tc.number : SUB_XXX_XXXX_XXXX // ✗ 错误:不应使用冒号
*/
it('testName', () => {
console.info('test001');
});
// 错误4: 注释结束行与用例间有空行
/**
* @tc.name testName
*/
// ✗ 错误:不应有空行
it('testName', () => {
console.info('test001');
});
// 错误5: 注释行未以*开始
/**
tc.name testName // ✗ 错误:未以*开始
* @tc.number SUB_XXX_XXXX
*/
it('testName', () => {
console.info('test001');
});
// 正确: 使用标准格式
/**
* @tc.name testName
* @tc.number SUB_XXX_XXXX_XXXX
* @tc.desc 测试用例描述
* @tc.size MEDIUMTEST
* @tc.type Function
* @tc.level Level2
*/
it('testName', TestType.FUNCTION | Size.MEDIUMTEST | Level.LEVEL2, async (done) => {
console.info('test001');
done();
});
| 类型编号 | 问题 | 检测方法 |
|---------|------|---------|
| 1 | 缺少 /** 开头 | 首行不以 /** 开始 |
| 2 | 缺少 */ 结尾 | 末行不包含 */ |
| 3 | 每行未以 * 开始 | 中间行不以 * 开始 |
| 4 | 参数名缺少 @ 修饰符 | 行内容匹配 tc.\w+ 但无 @ 前缀 |
| 5 | 使用冒号分隔符 | 行内容匹配 @tc.\w+\s*: |
| 6 | 注释与用例间有空行 | */ 与 it( 之间存在空行 |
--level warning 或 --level all.test.ets/.test.ts/.test.js),不扫描非测试源代码文件it(' 参数名挑战: 解析JSDoc注释并检查完整性
解决方案:
required_tags = [
'@tc.number', '@tc.name', '@tc.desc', '@tc.type',
'@tc.level', '@tc.size', '@tc.size', '@tc.since'
]
---
## 最新评估结果与实现状态(2026-04-14)
### 实现状态
**当前状态**: ❌ **未实现**(使用占位符扫描器)
**技术原因**:
- R008属于"模型生成规则",需要根据本文档的检测逻辑动态生成扫描代码
- 在 `scripts/main.py` 的 `load_rule_scanners()` 函数中,R008被替换为noop扫描器(空操作)
- 当前实现返回空列表,无法检测任何问题
### 改进方案
**方案1:实现为预置脚本(推荐)**
在 `scripts/simple_rules.py` 中添加R008的扫描实现:
```python
# ======================== R008: 用例声明格式不规范 ========================
# Source: rules/R008/SKILL.md
_R008_NO_AT_RE = re.compile(r'^\s*\*\s*(tc\.\w+)\s+\S')
def scan_r008(files, base_dir):
issues = []
for fp in files:
try:
with open(fp, 'r', encoding='utf-8', errors='ignore') as f:
content = f.read()
except Exception:
continue
lines = content.split('\n')
in_doc_comment = False
doc_start_line = 0
testcase_name = None
for i, line in enumerate(lines, 1):
stripped = line.strip()
# 检测 /** 开始
if stripped.startswith('/**'):
in_doc_comment = True
doc_start_line = i
continue
# 检测 */ 结束
if in_doc_comment and '*/' in stripped:
in_doc_comment = False
# 查找下一个 it() 调用
for j in range(i, min(i + 5, len(lines))):
m = re.search(r"\bit\s*\(\s*['\"]([^'\"]+)['\"]", lines[j])
if m:
testcase_name = m.group(1)
break
continue
# 在注释块内检测问题
if in_doc_comment:
# 检测缺少 @ 修饰符
m = _R008_NO_AT_RE.search(stripped)
if m:
issues.append(_make_issue(
'R008', '用例声明格式不规范', 'Warning',
fp, base_dir, i, line,
f'路径: {os.path.relpath(fp, base_dir)}, 行号: {i}, 问题描述: 参数 {m.group(1)} 缺少@修饰符',
testcase=testcase_name or '-'))
return issues
方案2:动态生成检测代码
修改 scripts/main.py,在扫描时根据本文档的检测逻辑动态生成并执行R008检测代码。
方案3:独立扫描脚本
创建独立的 scripts/scan_r008.py 文件,实现R008的完整检测逻辑。
根据 guides/R008_testcase_format/R008_FIX_GUIDE.md,R008支持自动修复:
修复内容:
tc.name 改为 @tc.nametc.number 改为 @tc.number修复命令:
python3 scripts/main.py /path/to/code --rules R008 --fix
详细评估报告见:/home/xianf/master/test/xts/evaluation_report.md
development
Run local code quality checks covering a subset of OpenHarmony gate CI (copyright, CodeArts C/C++) plus additional local checks (pylint/flake8, shellcheck/bashate, gn format). Use before committing to reduce gate failures. Triggers on: /oh-precommit-codecheck, "门禁检查", "门禁预检", "检查代码", "run codecheck", "check code quality", "lint my code", "代码检查", or after completing code implementation. WHEN to use: before git commit, before creating PR, after modifying C/C++/Python/Shell/GN files, when gate CI fails with codecheck defects, or when you want to preview what gate will flag.
development
OpenHarmony PR full lifecycle workflow. Five modes: - Commit: standardized commit with DCO sign-off and Issue linking - Create PR: commit + push to fork + create Issue + create PR on upstream - Fix Codecheck: fetch gate CI codecheck defects from a PR and auto-fix them - Review PR: fetch a PR's changes to local for code review - Fix Review: fetch unresolved review comments from a PR and auto-fix them Triggers on: /oh-pr-workflow, "提交代码", "创建PR", "提个PR", "commit", "修复告警", "修复门禁", "修复codecheck", "fix codecheck", "review pr", "review这个pr", "看下这个pr", "检视pr", "修复review", "修复检视意见", "fix review", or a GitCode PR URL with fix/review intent.
testing
分析 HM Desktop PRD 文档,提取需求信息、验证完整性、检查章节顺序(需求来源→需求背景→需求价值分析→竞品分析→需求描述)、检查 KEP 定义、检测需求冲突并生成结构化分析报告。适用于用户请求:(1) 分析或审查 PRD 文档, (2) 从需求中提取 KEP 列表, (3) 检查 PRD 完整性或一致性, (4) 将需求映射到模块架构, (5) 验证 PRD 格式合规性, (6) 验证竞品分析章节完整性。关键词:PRD分析, requirement extraction, KEP验证, completeness check, chapter order validation, 竞品分析检查, analyze PRD, 需求提取, 完整性检查, 章节顺序验证
development
基于 PRD 文档自动生成鸿蒙系统设计文档,包括架构设计文档和功能设计文档。生成前会分析 OpenHarmony 存量代码结构,确保与现有架构兼容。架构设计文档第2章必须为竞品方案分析,位于需求背景之后。适用于用户请求:(1) 生成架构设计文档, (2) 生成功能设计文档, (3) 从 PRD 生成设计文档, (4) 创建系统架构设计, (5) 编写功能规格说明, (6) 分析 OH 代码结构。关键词:architecture design, functional design, design doc, 竞品方案分析, OpenHarmony code analysis, 架构设计, 功能设计, 设计文档生成, OH代码分析, analyze codebase, competitor analysis