OpenHarmony 单元测试补全技能 (ohtest)
根据 .d.ts 接口文件(如 NAPI 生成的 Index.d.ts)在 ohosTest 的 test 文件夹下自动补全单元测试套件,参照 Ability.test.ets 结构,以接口为测试对象,生成符合 Hypium 规范的测试用例。
功能说明
- •输入:接口定义文件(如
entry/src/main/cpp/types/libentry/Index.d.ts)、测试目录(如entry/src/ohosTest/ets/test)、可选模块导入名(如libentry.so)。 - •输出:在 test 目录下新增一个
*Test.test.ets文件,并在现有List.test.ets(或主测试入口)中注册该测试套。 - •命名规则:接口文件名去掉特殊符号 +
Test。例如Index.d.ts→IndexdtsTest;函数名为indexdtsTest(),describe 套件名为IndexdtsTest。 - •测试套结构:与
Ability.test.ets一致:- •
export default function <name>Test() { - •
describe('<Name>Test', () => { ... }) - •默认包含
beforeAll、beforeEach、afterEach、afterAll。 - •每个接口方法对应多组
it('<method>_tc_N', 0, () => { ... })。
- •
四类边界测试用例
对每个 .d.ts 中的导出接口方法,生成 4 个用例:
| 类型 | 说明 | 示例(以 add(a: number, b: number) => number 为例) |
|---|---|---|
| 正常值 | 各种允许的输入类型、典型值 | add(1, 2),expect(result).assertEqual(3) |
| 最大值 | 输入类型的最大值 | add(Number.MAX_SAFE_INTEGER, 0),断言与预期一致 |
| 最小值 | 输入类型的最小值 | add(Number.MIN_SAFE_INTEGER, 0) 或负值边界 |
| 异常/压力 | 非数据类型、转换或大量调用 | 如 1000 次 add(1, 1),每次 expect(...).assertEqual(2) |
用例内调用接口方法,并用 expect(返回值).assertEqual(预期值)(或 assertContain 等)做断言。
代码结构与编码规范
生成测试用例时需遵守以下约定,本技能在生成代码时已落实基础实现。
代码结构
- •公共常量:测试中使用的数值、字符串等常量应集中在
constant.ets中定义并导出,测试文件通过import { ... } from './constant'引用。生成器在首次生成前会检查 test 目录下是否存在constant.ets,若不存在则自动创建并写入最小常量集(如HILOG_DOMAIN、TEST_FILTER、VAL_0~VAL_3、STRESS_1000、MAX、MIN等),后续可手动扩展。
编码规范
- •行宽:单行不超过 120 字符;过长注释或表达式应换行。
- •用例间隔:每个
it()用例块之间保留一个空行,便于阅读与 diff。 - •魔数:代码中不直接写魔数(如
0x0000、0、1000),改用constant.ets中的常量名(如HILOG_DOMAIN、TEST_FILTER、STRESS_1000)。 - •文件拆分:单个测试文件若超过 2000 行,应拆分为多个文件(如
Indexdts_test_1.ets、Indexdts_test_2.ets),并在主入口中按序引入;生成器目前不自动拆分,需人工处理超大文件。
生成内容已做到:使用 constant.ets 与导入常量、it() 间空行、hilog/expect 使用常量名、注释控制行宽;更多常量或拆分逻辑可在生成后手动补充。
UITest(页面 UI 测试)
根据 .ets 页面文件(如 pages/Index.ets)在 ohosTest 的 test 目录下生成 UI 测试套件,实现对页面的单元级 UI 测试。参考 HarmonyOS UITest 指南 与 arkXtest User Guide。
功能说明
- •输入:页面 .ets 文件路径(如
entry/src/main/ets/pages/Index.ets)、测试目录(如entry/src/ohosTest/ets/test)、可选 Ability 名称(默认EntryAbility)。 - •输出:在 test 目录下新增
<StructName>Ui.test.ets(如IndexUi.test.ets),并在List.test.ets中注册该测试套。 - •解析内容:从页面中解析
struct名、@State初始文本、Text(this.xxx)/Text('literal')、.onClick内this.xxx = 'yyy'、以及Row()/Column()布局。 - •生成用例:
- •页面加载:断言当前 Top Ability 为指定 Ability。
- •布局:断言存在
Row/Column(ON.type('Row')/ON.type('Column'))。 - •控件:对每个初始显示的文本断言存在(
ON.text('...')、assertComponentExist)。 - •动作:对每个带
onClick且会改变@State的控件,生成「findComponent → click → assertComponentExist(变化后文本)」用例。
- •框架:使用
@kit.TestKit的Driver、ON、abilityDelegatorRegistry,以及@ohos/hypium的describe/it/expect;常量从constant.ets引入(TEST_FILTER、UI_DELAY_MS)。
何时使用
- •用户说:「对 Index.ets 实现 UI 测试」「为页面生成 UITest」「根据页面控件和布局写 UI 单元测试」。
- •需要对 ArkUI 页面做自动化 UI 测试:页面加载、布局存在、控件存在、点击后状态/文案变化。
使用方式
# 必选:页面 .ets 文件、测试目录;可选:Ability 名、是否更新 List.test.ets python3 src/skills/ohtest/uitest_gen.py \ --ets /path/to/entry/src/main/ets/pages/Index.ets \ --test-dir /path/to/entry/src/ohosTest/ets/test \ [--ability-name EntryAbility] \ [--no-update-list]
生成文件命名:<StructName>Ui.test.ets(如 IndexUi.test.ets),套件名为 <StructName>UiTest(如 IndexUiTest)。若 test 目录下已有 constant.ets,生成器会追加 UI_DELAY_MS(若缺失),与 dts 单元测试共用同一 constant 文件。
参照与限制
- •参照:HarmonyOS UITest 指南、arkXtest UiTest(Driver.create、findComponent(ON.text)、click、assertComponentExist)。
- •限制:解析基于简单正则,仅识别
@State字符串、Text(this.xxx)/Text('literal')、.onClick内this.xxx = 'yyy'及Row()/Column();复杂表达式或动态文本需生成后人工补充用例。
何时使用(dts 单元测试)
- •用户说:「根据 Index.d.ts 补全/生成单元测试」「为 libentry 接口写测试」「在 ohtest 里增加以 .d.ts 为对象的测试套」。
- •需要以 NAPI/TS 接口为对象,在 ohosTest 下快速生成符合规范的边界测试用例时。
使用方式(dts 单元测试)
# 必选:接口定义文件、测试目录;可选:模块名(默认 libentry.so)、是否更新 List.test.ets python3 src/skills/ohtest/ohtest.py \ --dts /path/to/entry/src/main/cpp/types/libentry/Index.d.ts \ --test-dir /path/to/entry/src/ohosTest/ets/test \ [--module libentry.so] \ [--no-update-list]
生成文件命名:<基名>.test.ets,基名为接口文件名去掉特殊符号(如 Index.d.ts → Indexdts),故得 Indexdts.test.ets;套件名为基名+Test(如 IndexdtsTest),describe 与 export default function 分别为 IndexdtsTest、indexdtsTest。
参照模板
- •结构参照:
Ability.test.ets(export default function abilityTest()、describe('ActsAbilityTest', () => { ... })、beforeAll/beforeEach/afterEach/afterAll、it('assertContain', 0, () => { ... expect(...).assertEqual(...) }))。 - •测试对象:来自
Index.d.ts的导出接口(如add),导入方式与工程一致(如import lib from 'libentry.so',调用lib.add(...))。
Fuzz 测试执行(fuzztest.py)
在正确的工作目录和环境(含 hdc 路径)下调用 developer_test 的 start.sh 执行 FUZZ 测试套。
环境与路径
- •工作目录:执行时在
test/testfwk/developer_test下调用./start.sh。 - •hdc:框架通过
shutil.which("hdc")查找 hdc。脚本会将${OHOS_SDK_PATH}/linux/toolchains(及toolchains/bin若存在)加入PATH,以便框架找到 hdc;未设置OHOS_SDK_PATH时需保证系统PATH中已有 hdc。 - •DEVTESTDIR:脚本会设置
DEVTESTDIR为 developer_test 的绝对路径,与框架约定一致。
何时使用
- •用户说:「执行 GetAppStatsMahFuzzTest 的 fuzz 测试」「跑 FUZZ 用例」「执行 fuzztest」。
- •需要在本机通过 developer_test 框架在设备上跑指定 FUZZ 测试套时。
使用方式
# 必选:-ts 测试套名;可选:-p 产品名、--coverage、--dry-run python3 src/.claude/skills/ohtest/fuzztest.py run -ts GetAppStatsMahFuzzTest python3 src/.claude/skills/ohtest/fuzztest.py run -ts GetAppStatsMahFuzzTest -p rk3568 --coverage python3 src/.claude/skills/ohtest/fuzztest.py run -ts GetAppStatsMahFuzzTest --dry-run python3 src/.claude/skills/ohtest/fuzztest.py help
| 参数 | 说明 |
|---|---|
-ts / --testsuite | 测试套名(必填),如 GetAppStatsMahFuzzTest |
-p / --product | 产品名,默认 rk3568 |
--coverage | 附加 -cov coverage 收集覆盖率 |
--dry-run | 仅打印将要执行的命令,不实际执行 |
执行前需:设备已连接、hdc 可用(设置 OHOS_SDK_PATH 或系统 PATH 中含 hdc)。
分析 fuzztest 测试覆盖率(coverage_analysis.py)
对已跑过带覆盖率 fuzz 测试的设备,收集 gcda、生成 .gcov 并统计覆盖率。
流程
- •在设备上跑带覆盖率的 fuzz 测试(若尚未跑过)
python3 src/.claude/skills/ohtest/fuzztest.py run -ts GetAppStatsMahFuzzTest --coverage - •收集覆盖率并生成报告
python3 src/.claude/skills/ohtest/coverage_analysis.py run [-p rk3568]
会从设备上两个路径查找 *.gcda:① 本地根(由本地路径前两级推导,如/root/ohos);②/data/gcov/<本地根>(如/data/gcov/root/ohos)。拷贝到 reports/obj 后,再拷贝对应 *.gcno 与源码,在 reports/obj 内执行 gcov。 - •查看覆盖率统计
python3 src/.claude/skills/ohtest/coverage_analysis.py analyze
解析 reports/obj 下的 .gcov 并输出可执行行、已覆盖行、覆盖率百分比及等级。
环境
- •hdc:与 fuzztest 相同,脚本会将
${OHOS_SDK_PATH}/linux/toolchains(及toolchains/bin)加入 PATH;未设置时需保证系统 PATH 中已有 hdc。 - •执行
run前需设备已连接且已跑过带-cov coverage的 fuzz 测试。
技能 1:清除分析结果并再次分析
清除 reports/obj 下的覆盖率相关文件(.gcda、.gcno、.cpp、.gcov),再从设备拉取 gcda、拷贝 gcno/cpp、执行 gcov,最后执行 analyze 输出统计。
python3 src/.claude/skills/ohtest/coverage_analysis.py clear-analyze python3 src/.claude/skills/ohtest/coverage_analysis.py clear-analyze -p rk3568
技能 2:清除分析结果、重新运行 fuzztest 并分析
先清除 reports/obj;再在设备上执行带覆盖率的 fuzz 测试(调用 fuzztest.py);然后从设备拉取 gcda、生成 .gcov;最后执行 analyze 输出统计。
python3 src/.claude/skills/ohtest/coverage_analysis.py clear-rerun-fuzz-analyze python3 src/.claude/skills/ohtest/coverage_analysis.py clear-rerun-fuzz-analyze -ts GetAppStatsMahFuzzTest -p rk3568
| 参数 | 说明 |
|---|---|
-ts / --testsuite | fuzz 测试套名,默认 GetAppStatsMahFuzzTest |
-p / --product | 产品名,默认 rk3568 |
--device | 指定设备 ID |
--search-root | 设备上查找 *.gcda 的目录,可多次指定 |