R016: testcase命名规范
SkillDev toolsA skill for dev tools by openharmonyinsight.
Available today. Use it from your connected AI after setup.
No other account needed.
Connect ahel once, and every AI you use reads what you have installed.
Then ask your AI: use the R016: testcase命名规范 skill
What this skill tells your AI
The instructions your AI receives, as published by openharmonyinsight/openharmony-skills in skills/check-test-code-quality/rules/R016/SKILL.md and read by ahel’s review.
- 规则编号: R016
- 严重级别: Warning
- 规则复杂度: complex(需要代码分析)
- 问题类型: testcase名称包含特殊字符(仅允许英文字母、数字、下划线、连字符)
- 修复方式: 移除特殊字符,追加
Adapt+三位数字后缀,同步修改@tc.name - 预期问题数量: 400+
扫描范围
仅扫描测试文件:
.test.ets- ArkTS测试文件.test.ts- TypeScript测试文件.test.js- JavaScript测试文件
问题描述
testcase名称(it()的第一个参数)中包含特殊字符。仅允许使用英文字母(a-zA-Z)、数字(0-9)、下划线(_)和连字符(-)。
检测对象: 以it()的第一个参数为准,不检测@tc.name的值。@tc.name仅在修复阶段同步修改。
检测逻辑
核心算法
import re
TC_NAME_PATTERN = re.compile(r'^[a-zA-Z0-9_-]+$')
IT_PATTERN = re.compile(r'\bit\s*\(\s*([\'"])([^\'"]+)\1')
TC_NAME_ANNOTATION = re.compile(r'@tc\.name\s+(.+)')
def scan_r016(file_path: str, content: str) -> list:
issues = []
lines = content.split('\n')
it_entries = extract_all_it_entries(lines)
for entry in it_entries:
tc_name = entry['it_tc_name']
it_line_idx = entry['line_idx']
if TC_NAME_PATTERN.match(tc_name):
continue
# testcase名称包含特殊字符
snippet = lines[it_line_idx].strip()
if len(snippet) > 120:
snippet = snippet[:120] + '...'
# 生成修复建议
suggestion = build_r016_suggestion(file_path, it_line_idx + 1, tc_name, lines, entry)
issues.append({
'rule': 'R016',
'type': 'testcase命名规范',
'severity': 'Warning',
'file': file_path,
'line': it_line_idx + 1,
'testcase': tc_name,
'snippet': snippet,
'suggestion': suggestion,
})
return issues
提取所有it()条目
def extract_all_it_entries(lines: list) -> list:
"""
提取文件中所有it()函数调用及其对应的@tc.name注解。
Returns:
[{'line_idx': int, 'tc_name': str, 'tc_name_line_idx': int|None, 'has_tc_annotation': bool}]
"""
entries = []
i = 0
while i < len(lines):
line = lines[i]
# 匹配it()函数调用
match = IT_PATTERN.search(line)
if match:
it_tc_name = match.group(2)
tc_name_line_idx = None
has_tc_annotation = False
final_tc_name = it_tc_name
# 向上查找最近的 @tc.name 注解
for back in range(i - 1, max(i - 20, -1), -1):
back_line = lines[back].strip()
if not back_line:
continue
if back_line.startswith('* @tc.name') or back_line.startswith('@tc.name'):
tc_match = re.search(r'@tc\.name\s+(.+)', back_line)
if tc_match:
annotated_name = tc_match.group(1).strip()
final_tc_name = annotated_name
tc_name_line_idx = back
has_tc_annotation = True
break
# 遇到注释块开始或非注释行,停止向上搜索
if back_line.startswith('/**') or back_line.startswith('/*'):
break
if not back_line.startswith('*') and not back_line.startswith('//'):
break
entries.append({
'line_idx': i,
'tc_name': final_tc_name,
'it_tc_name': it_tc_name,
'tc_name_line_idx': tc_name_line_idx,
'has_tc_annotation': has_tc_annotation,
})
i += 1
return entries
修复建议生成
def build_r016_suggestion(file_path: str, line_num: int, tc_name: str,
lines: list, entry: dict) -> str:
"""
生成R016修复建议,包含具体的修复方案。
修复规则:
1. 移除testcase名称中所有非[a-zA-Z0-9_-]的字符
2. 移除后追加Adapt+三位数字后缀(从001开始)
3. 如果追加后命名存在重复,数字递增
4. 同步修改@tc.name值(如果有)
5. 仅修改it()和@tc.name,不修改@tc.number、@tc.desc等
"""
# 步骤1:移除特殊字符
cleaned = re.sub(r'[^a-zA-Z0-9_-]', '', tc_name)
# 步骤2:收集文件中已有的testcase名称,确保不重复
existing_names = set()
for e in extract_all_it_entries(lines):
existing_names.add(e['tc_name'])
# 步骤3:追加Adapt+三位数字后缀,确保唯一
new_name = None
for suffix_num in range(1, 1000):
candidate = f"{cleaned}Adapt{suffix_num:03d}"
if candidate not in existing_names:
new_name = candidate
break
if new_name is None:
new_name = f"{cleaned}Adapt999"
suggestion_parts = [
f"路径: {file_path}, 行号: {line_num}, ",
f"问题描述: testcase名称 '{tc_name}' 包含特殊字符。",
f"仅允许英文字母、数字、下划线、连字符。",
]
if entry['has_tc_annotation']:
suggestion_parts.append(
f"修复: 将it()参数和@tc.name修改为 '{new_name}'。"
)
else:
suggestion_parts.append(
f"修复: 将it()参数修改为 '{new_name}'。"
)
return ''.join(suggestion_parts)
修复规则
修复步骤
- 移除特殊字符:从testcase名称中移除所有非
[a-zA-Z0-9_-]的字符 - 追加后缀:在清理后的名称后追加
Adapt+三位数字后缀(从001开始) - 去重检查:如果追加后命名在文件内已存在,数字递增(
001->002->003...) - 同步@tc.name:
it()参数变动时,必须同步修改对应的@tc.name值,保持一致 - 仅修改必要字段:只修改
it()和@tc.name,不修改@tc.number、@tc.desc等其他字段
修复示例
// 修复前
/**
* @tc.name test.name@001
* @tc.number SUB_TEST_0100
* @tc.desc 测试用例描述
*/
it('test.name@001', Level.LEVEL0, () => {
// ✗ 错误:包含@和.字符
});
// 修复后
/**
* @tc.name testname001Adapt001
* @tc.number SUB_TEST_0100 ← 不修改
* @tc.desc 测试用例描述 ← 不修改
*/
it('testname001Adapt001', Level.LEVEL0, () => {
// ✓ 正确:移除了特殊字符并追加Adapt后缀
});
错误示例
// 错误1:包含@和.字符
it('test.name@001', Level.LEVEL0, () => {
// ✗ 错误:包含@和.字符
});
// 错误2:包含空格
it('test name 001', Level.LEVEL0, () => {
// ✗ 错误:包含空格
});
// 错误3:包含#字符
it('test#name001', Level.LEVEL0, () => {
// ✗ 错误:包含#字符
});
// 错误4:包含中文
it('测试用例001', Level.LEVEL0, () => {
// ✗ 错误:包含中文字符
});
// 错误5:包含括号
it('test(001)', Level.LEVEL0, () => {
// ✗ 错误:包含括号
});
// 错误6:包含冒号和斜杠
it('test:api/v1', Level.LEVEL0, () => {
// ✗ 错误:包含冒号和斜杠
});
正确示例
// 正确1:仅使用英文字母和数字
it('testName001', Level.LEVEL0, () => {
// ✓ 正确:符合命名规范
});
// 正确2:使用下划线分隔
it('test_name_001', Level.LEVEL0, () => {
// ✓ 正确:符合命名规范
});
// 正确3:使用连字符分隔
it('test-name-001', Level.LEVEL0, () => {
// ✓ 正确:符合命名规范
});
// 正确4:混合使用字母、数字、下划线和连字符
it('testFunc_API_v2-001', Level.LEVEL0, () => {
// ✓ 正确:符合命名规范
});
输出格式
问题数据结构
{
'rule': 'R016',
'type': 'testcase命名规范',
'severity': 'Warning',
'file': 'rel/path.test.ets',
'line': 25,
'testcase': 'test.name@001',
'snippet': "it('test.name@001', Level.LEVEL0, () => {",
'suggestion': '路径: rel/path.test.ets, 行号: 25, 问题描述: testcase名称 \'test.name@001\' 包含特殊字符。仅允许英文字母、数字、下划线、连字符。修复: 将it()参数和@tc.name修改为 \'testname001Adapt001\'。'
}
Excel报告列
| 列序 | 列名 | 示例 |
|---|---|---|
| 1 | 问题ID | R016 |
| 2 | 问题类型 | testcase命名规范 |
| 3 | 严重级别 | Warning |
| 4 | 文件路径 | web/DFX/log/entry/src/ohosTest/ets/test/Test.test.ets |
| 5 | 行号 | 25 |
| 6 | 所属用例 | test.name@001 |
| 7 | 代码片段 | it('test.name@001', Level.LEVEL0, () => { |
| 8 | 修复建议 | 路径: ..., 行号: 25, 问题描述: testcase名称 'test.name@001' 包含特殊字符... |
陷阱与注意事项
1. @tc.name与it()参数不一致
有些文件中@tc.name的值与it()第一个参数不一致,此时应以@tc.name为准进行检测。
2. 仅修改it()和@tc.name
修复时只修改it()的第一个参数和对应的@tc.name注解值,不得修改:
@tc.number- 用例编号@tc.desc- 用例描述@tc.size- 用例规模@tc.type- 用例类型@tc.level- 用例级别
3. Adapt后缀去重
追加Adapt后缀后,需要在当前文件范围内检查是否与已有testcase名称冲突。如果冲突,数字递增。
4. 清理后名称为空
如果移除所有特殊字符后名称为空(如@#$%),则使用unnamedTest作为基础名称,再追加Adapt后缀。
5. 严禁使用命名格式检测代替特殊字符检测(极严重)
- 严重性: 极严重,曾导致print子系统313条R016全部误报(0%准确率)
- 问题: 将R016错误实现为"检查testcase名称是否符合
testXxx或IT_xxx格式",用正则^(test|IT|it)[A-Z]\w*$做格式匹配。 - 正确实现: 必须使用
^[a-zA-Z0-9_-]+$做正向字符集匹配。
详见 references/TRAPS.md 陷阱8。
6. 不得用@tc.name的值作为检测源(极严重)
R016的检测对象是it()的第一个参数,不是@tc.name注解的值。@tc.name仅在修复阶段同步修改。
详见 references/TRAPS.md 陷阱9。
错误/正确示例
来源: EXAMPLES.md
以下为补充示例(与上方已有示例互补):
错误示例:
// 错误:包含特殊字符
it('test.name@001', () => { // ✗ 错误:包含@字符
console.info('test001');
});
// 错误:包含空格
it('test name 001', () => { // ✗ 错误:包含空格
console.info('test001');
});
// 错误:包含其他特殊字符
it('test#name001', () => { // ✗ 错误:包含#字符
console.info('test001');
});
正确示例:
// 正确:仅使用英文字母、数字、下划线和连字符
it('test_name_001', () => { // ✓ 正确:符合命名规范
console.info('test001');
});
it('testName001', () => { // ✓ 正确:符合命名规范
console.info('test001');
});
it('test-name-001', () => { // ✓ 正确:符合命名规范
console.info('test001');
});
实现细节
来源: IMPLEMENTATION_DETAILS.md
命名规范: testcase名称只能包含英文字母、数字、下划线、连字符
def check_testcase_naming(tc_name: str):
# 只允许英文字母、数字、下划线、连字符
if not re.match(r'^[a-zA-Z0-9_-]+$', tc_name):
report_issue("testcase名称包含特殊字符")
技术挑战与解决方案
来源: V3_UPGRADE_GUIDE.md
- 预期问题数量: 414个
- 实现方式: 名称提取 + 特殊字符检测
- 允许字符: a-zA-Z0-9_-
Signals
- GitHub stars
- 34
- Forks
- 7
- Last commit
- Sep 2026
Advanced
- Catalog kind
- skill
- Gateway key
r016- Source
- github.com/openharmonyinsight/openharmony-skills