skills/check-test-code-quality/rules/R003/SKILL.md
# R003: 禁止恒真断言 ## 规则概述 | 属性 | 值 | |------|-----| | 规则编号 | R003 | | 问题类型 | 禁止恒真断言 | | 严重级别 | Critical | | 规则分类 | 简单规则(正则匹配) | | 扫描范围 | 所有源代码文件(`.ets`, `.ts`, `.js`) | ## 问题描述 测试用例中使用 `expect(true).assertTrue()` 等恒真断言,断言只会产生成功结果,用例恒通过。此类断言未对接口实际返回值进行断言校验,相当于没有任何有效断言。 ## 检测模式(完整覆盖) R003规则必须检测以下**全部3种**恒真断言模式: ```python r003_patterns = [ # 模式1: expect(true).assertTrue() # 最常见的恒真断言形式,true断言为true,恒成立 re.compile(r'expect\s*\(\s*true\s*\)\s*\.\s*assertTrue\s*\('), # 模式2: expect(t
npx skillsauth add openharmonyinsight/openharmony-skills skills/check-test-code-quality/rules/R003Install 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.
| 属性 | 值 |
|------|-----|
| 规则编号 | R003 |
| 问题类型 | 禁止恒真断言 |
| 严重级别 | Critical |
| 规则分类 | 简单规则(正则匹配) |
| 扫描范围 | 所有源代码文件(.ets, .ts, .js) |
测试用例中使用 expect(true).assertTrue() 等恒真断言,断言只会产生成功结果,用例恒通过。此类断言未对接口实际返回值进行断言校验,相当于没有任何有效断言。
R003规则必须检测以下全部3种恒真断言模式:
r003_patterns = [
# 模式1: expect(true).assertTrue()
# 最常见的恒真断言形式,true断言为true,恒成立
re.compile(r'expect\s*\(\s*true\s*\)\s*\.\s*assertTrue\s*\('),
# 模式2: expect(true).assertEqual(true) ⚠️ 容易遗漏!
# 128个问题,占R003总量的3.3%。true等于true,恒成立
# 如果遗漏此模式,将导致约2014个问题被漏报
re.compile(r'expect\s*\(\s*true\s*\)\s*\.\s*assertEqual\s*\(\s*true\s*\)'),
# 模式3: expect(false).assertFalse()
# false断言为false,恒成立
re.compile(r'expect\s*\(\s*false\s*\)\s*\.\s*assertFalse\s*\('),
]
| 模式 | 代码形式 | 预估问题数 | 占比 | 遗漏影响 |
|------|---------|-----------|------|---------|
| 模式1 | expect(true).assertTrue() | ~3,800 | ~96.7% | 主要模式 |
| 模式2 | expect(true).assertEqual(true) | ~128 | ~3.3% | 易遗漏 |
| 模式3 | expect(false).assertFalse() | ~6 | ~0.0% | 罕见 |
问题严重性: ⭐⭐⭐⭐ 严重
expect(true).assertEqual(true) 是恒真断言的变体,容易被遗漏。该模式虽然占比仅3.3%(128个),但遗漏会导致约2014个相关文件的问题被完全忽略。必须将所有3种模式都加入检测列表。
详见 references/TRAPS.md 陷阱6。
it('testCounter01', Level.LEVEL0, async (done: Function) => {
console.info('[testCounter01] START');
let strJson = getInspectorByKey('Counter');
let obj: ESObject = JSON.parse(strJson);
console.info("[testCounter01] obj is : " + JSON.stringify(obj.$attrs));
expect(true).assertTrue(); // ✗ 恒真断言:true断言为true,恒成立
console.info('[testCounter01] END');
done();
});
来源: 用例低级问题.md 第⑤点
it('testIsLocationEnabled06', TestType.FUNCTION | Size.MEDIUMTEST | Level.LEVEL2, function () {
try {
let state = geolocationm.isLocationEnabled();
console.info('[lbs_js] getLocationSwitchState06 result: ' + JSON.stringify(state));
expect(true).assertEqual(state); // ✗ 此处是对state的断言,但如果state恰好为true...
} catch (error) {
console.info("[lbs_js] getLocationSwitchState06 try err." + JSON.stringify(error));
if (error.code == "801") {
expect(error.code).assertEqual("801")
} else {
expect().assertFail();
}
}
});
来源: 用例低级问题.md 第②点
it('effectKitTest_static_0100', TestType.FUNCTION | Size.MEDIUMTEST | Level.LEVEL1,
async (done: () => void): Promise<void> => {
// ...
try {
let headFilter: effectKit.Filter = effectKit.createEffect(pixelMap);
console.info('www data succeed')
if (headFilter == undefined) {
hilog.info(domain, tag, '%{public}s', 'effectKit createFilter failed.');
return undefined;
}
let blurFilter: effectKit.Filter = headFilter.blur(10);
expect(true).assertTrue(); // ✗ 恒真断言(模式1)
hilog.info(domain, tag, '%{public}s', 'effectKit createFilter successfully.');
const effectPixelMap = await blurFilter.getEffectPixelMap();
if (effectPixelMap != undefined) {
hilog.info(domain, tag, '%{public}s', 'effectKit getEffectPixelMap successfully.');
} else {
hilog.info(domain, tag, '%{public}s', 'effectKit getEffectPixelMap failed.');
}
hilog.info(domain, tag, '%{public}s', 'effectKit test END');
expect(true).assertTrue(); // ✗ 恒真断言(模式1),出现两次
} catch (err) {
console.info('www data err ' + JSON.stringify(err))
}
done();
})
来源: 用例低级问题.md 第②点
it('testBoolCheck', Level.LEVEL0, () => {
let result = someFunction();
if (result === false) {
expect(false).assertFalse(); // ✗ 恒真断言:false断言为false,恒成立
}
});
it('test001', Level.LEVEL0, () => {
let actualValue = someFunction();
expect(actualValue).assertTrue(); // ✓ 断言实际值,非恒真
});
it('test002', Level.LEVEL0, () => {
let actualValue = someFunction();
expect(actualValue).assertFalse(); // ✓ 断言实际值为false
});
it('test003', Level.LEVEL0, () => {
let actualValue = someFunction();
expect(actualValue).assertEqual('expected'); // ✓ 断言具体期望值
});
it('test004', Level.LEVEL0, () => {
let count = 900;
expect(count).assertLargerOrEqual(800); // ✓ 断言count >= 800
});
it('test005', Level.LEVEL0, async (done: Function) => {
try {
await someAsyncFunction();
expect(true).assertTrue(); // ✗ 仍然是R003问题(恒真断言)
done();
} catch (error) {
expect(error.code).assertEqual(401); // ✓ catch块中有有效断言
done();
}
});
注意: 即使try-catch结构中catch块有有效断言(如error.code),try块中的
expect(true).assertTrue()仍然属于R003问题,因为该断言本身是恒真的。正确做法是移除try块中的恒真断言,或替换为对实际返回值的有效断言。
def check_r003(file_path: str, content: str, lines: list[str]) -> list[dict]:
"""
扫描R003: 禁止恒真断言
Args:
file_path: 文件相对路径
content: 文件完整内容
lines: 文件按行分割的列表
Returns:
问题列表
"""
issues = []
r003_patterns = [
re.compile(r'expect\s*\(\s*true\s*\)\s*\.\s*assertTrue\s*\('),
re.compile(r'expect\s*\(\s*true\s*\)\s*\.\s*assertEqual\s*\(\s*true\s*\)'),
re.compile(r'expect\s*\(\s*false\s*\)\s*\.\s*assertFalse\s*\('),
]
# 解析it()块范围,用于填充testcase字段
it_blocks = parse_it_blocks(content, lines)
for i, line in enumerate(lines, 1):
for pattern in r003_patterns:
match = pattern.search(line)
if match:
# 提取匹配到的代码片段
snippet = line.strip()
# 确定所属用例
testcase = find_testcase_for_line(it_blocks, i)
# 判断恒真类型
if 'assertEqual' in snippet:
suggestion = (
"禁止恒真断言。expect(true).assertEqual(true) 恒成立,"
"未对接口实际返回值进行断言校验。"
"请替换为对实际业务逻辑的有效断言,"
"如 expect(actualValue).assertEqual(expectedValue)。"
)
elif 'assertFalse' in snippet:
suggestion = (
"禁止恒真断言。expect(false).assertFalse() 恒成立,"
"请替换为对实际变量值的断言,"
"如 expect(actualValue).assertFalse()。"
)
else:
suggestion = (
"禁止恒真断言。expect(true).assertTrue() 恒成立,"
"未对接口实际返回值进行断言校验。"
"请替换为对实际业务逻辑的有效断言,"
"如 expect(actualValue).assertTrue() 或 expect(actualValue).assertEqual(expected)。"
)
issues.append({
'rule': 'R003',
'type': '禁止恒真断言',
'severity': 'Critical',
'file': file_path,
'line': i,
'testcase': testcase,
'snippet': snippet,
'suggestion': suggestion,
})
break # 同一行只报告一次(优先匹配第一个模式)
return issues
所有R003扫描结果必须包含 testcase 字段,用于标识问题所属的测试用例:
def find_testcase_for_line(it_blocks: list[dict], line_num: int) -> str:
"""
查找指定行号所属的it()块,返回testcase名称。
Args:
it_blocks: it()块列表,每个元素包含 {'name': str, 'start': int, 'end': int}
line_num: 问题所在行号
Returns:
testcase名称,不在任何it()块内时返回 '-'
"""
for block in it_blocks:
if block['start'] <= line_num <= block['end']:
return block['name']
return '-'
| 列序 | 列名 | 示例 |
|------|------|------|
| 1 | 问题ID | R003 |
| 2 | 问题类型 | 禁止恒真断言 |
| 3 | 严重级别 | Critical |
| 4 | 文件路径 | arkui/ace_ets_component_seven/.../Counter.test.ets |
| 5 | 行号 | 213 |
| 6 | 所属用例 | testCounter01 |
| 7 | 代码片段 | expect(true).assertTrue(); |
| 8 | 修复建议 | 禁止恒真断言。expect(true).assertTrue() 恒成立,未对接口实际返回值进行断言校验。请替换为对实际业务逻辑的有效断言... |
[R003] arkui/ace_ets_component_seven/ace_ets_component_seven_special/entry/src/main/ets/test/Counter.test.ets:213
用例: testCounter01
代码: expect(true).assertTrue();
建议: 禁止恒真断言。expect(true).assertTrue() 恒成立,未对接口实际返回值进行断言校验。
请替换为对实际业务逻辑的有效断言,如 expect(actualValue).assertTrue() 或 expect(actualValue).assertEqual(expected)。
来源: 规则内置示例(原 docs/EXAMPLES.md,已迁移)
以下断言方法均可用于替代恒真断言:
it('test003', Level.LEVEL0, () => {
let value = 50;
expect(value).assertLessOrEqual(100); // ✓ 正确:断言value <= 100
});
it('test004', Level.LEVEL0, () => {
let result = 100;
expect(result).assertLarger(50); // ✓ 正确:断言result > 50
});
it('test005', Level.LEVEL0, () => {
let timeout = 5000;
expect(timeout).assertLess(10000); // ✓ 正确:断言timeout < 10000
});
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