鸿蒙 PC Markdown 编辑器原生测试:ohosTest 验证文档与大纲服务
鸿蒙 PC Markdown 编辑器原生测试:ohosTest 验证文档与大纲服务
Web自动化可以证明 CodeMirror和预览逻辑,却无法证明 HarmonyOS Core File Kit在目标运行时的真实读写语义。BOM、fsync、AtomicFile、应用沙箱和 ArkTS字符串偏移必须在 ohosTest环境执行。否则 Node测试全绿,设备文件仍可能短写或格式变化。
本文基于 OhMarkdown,说明 Hypium测试模块、沙箱隔离、字节读取、故障注入、大纲偏移和设备结果。代码位于 https://gitcode.com/VON-/codex_md_oh,对应提交 3a9146e。
测试模块独立于 entry 主模块
entry/src/ohosTest/module.json5:
{
"module": {
"name": "entry_test",
"type": "feature",
"deviceTypes": [
"phone",
"2in1"
],
"deliveryWithInstall": true,
"installationFree": false
}
}
测试目标同时声明 phone与2in1,当前重点在 MateBook Pro 2in1模拟器。入口文件只注册服务测试:
import documentServiceTest
from './DocumentService.test';
export default function testsuite() {
documentServiceTest();
}
保持入口简单,测试分组由具体文件负责。新增 Workspace或 Recovery测试时可独立文件注册,不把所有逻辑堆在 List.test。
使用应用测试上下文
const context = abilityDelegatorRegistry
.getAbilityDelegator()
.getAppContext();
const testPath =
`${context.filesDir}/${TEST_FILE_NAME}`;
测试文件位于测试应用沙箱,不污染用户 Documents,也不依赖系统选择器。使用真实 context.filesDir让 RecoveryService路径与生产一致。
沙箱测试不能替代用户 provider URI,但适合可重复字节和故障场景。选择器授权仍在模拟器手工/自动化层验证。
beforeEach 和 afterEach 双重清理
beforeEach(async () => {
if (await fileIo.access(testPath)) {
await fileIo.unlink(testPath);
}
if (await fileIo.access(faultPath)) {
await fileIo.unlink(faultPath);
}
if (await fileIo.access(faultDirectory)) {
await fileIo.rmdir(faultDirectory);
}
await clearPendingSaveBackup(context.filesDir);
});
afterEach重复相同清理。before保证上次异常中断不影响当前用例,after保证成功测试不影响下一项。异步文件操作全部 await,不能让清理与测试并发。
删除顺序先文件后目录,避免非空目录 rmdir失败。恢复备份是共享沙箱资源,也必须清理。
测试辅助写入不复用被测函数
async function writeRawText(
path: string,
content: string
): Promise<void> {
const file = await fileIo.open(
path,
fileIo.OpenMode.CREATE |
fileIo.OpenMode.READ_WRITE
);
try {
const writtenBytes = await fileIo.write(
file.fd,
content,
{ offset: 0, encoding: 'utf-8' }
);
await fileIo.truncate(file.fd, writtenBytes);
await fileIo.fsync(file.fd);
} finally {
await fileIo.close(file);
}
}
构造输入不调用 writeUtf8Document,否则用被测序列化器生成期望文件,会让同一缺陷同时存在于准备和验证。辅助函数只按给定字符串原样写 UTF-8。
更严格可断言 writtenBytes等于预期,测试辅助自身也要可靠。当前小语料在设备验证中稳定。
字节比较绕过解码
const stat = await fileIo.stat(file.fd);
const data = new ArrayBuffer(stat.size);
const bytesRead = await fileIo.read(
file.fd,
data,
{ length: stat.size }
);
const bytes = new Uint8Array(
data,
0,
bytesRead
);
let result = '';
for (let index = 0;
index < bytes.length;
index += 1) {
result += bytes[index]
.toString(16)
.padStart(2, '0');
}
返回十六进制用于 assertEqual。BOM与 CRLF不能通过再次调用 readUtf8Document验证,因为读取器会抽象它们;必须比较原始字节。
生产测试若扩展到大文件,应用哈希更节省内存。当前数十字节 fixture用完整 hex能在失败时直接看到差异。
BOM 与 CRLF 用例
it('UTF-8 BOM 与 CRLF 保存后字节一致',
0,
async () => {
const original =
'\uFEFF# 鸿蒙 PC\r\n\r\n' +
'第一行\r\n第二行\r\n';
await writeRawText(testPath, original);
const beforeHex = await readBytesAsHex(testPath);
const opened = await readUtf8Document(testPath);
expect(opened.format.hasUtf8Bom).assertTrue();
expect(opened.format.lineEnding)
.assertEqual(LineEnding.CRLF);
await writeUtf8Document(
testPath,
opened.content,
opened.format
);
expect(await readBytesAsHex(testPath))
.assertEqual(beforeHex);
}
);
它同时验证读取格式元数据、内存正文去 BOM、保存序列化和 Core File Kit写入。字节相等是最终门槛。
用例名使用中文,设备报告直接表达目标。第二个参数0是测试过滤/级别参数,异步函数由 Hypium等待。
Mixed 换行归一
输入包含 CRLF、LF和单独 CR,读取应为 MIXED。指定 LF保存后重新读取,正文必须全是 \n,检测为 LF。
该用例验证的是服务显式转换,不涉及 UI策略对话框。UI取消、LF、CRLF三按钮需要模拟器交互测试。测试层必须说明覆盖范围,不能把服务用例冒充完整工作流。
目录删除制造确定故障
await fileIo.mkdir(faultDirectory);
await writeRawText(faultPath, previousContent);
await savePendingSaveBackup(
context.filesDir,
{
version: 1,
documentUri: faultPath,
documentName: TEST_FILE_NAME,
previousContent,
hasUtf8Bom: false,
lineEnding: LineEnding.LF,
updatedAt: Date.now()
}
);
await fileIo.unlink(faultPath);
await fileIo.rmdir(faultDirectory);
目标父目录不存在,writeUtf8Document必然失败。相比模拟磁盘满,目录删除易重复且不影响设备全局。异常后断言 backup仍能加载、previousContent正确,再重建目录并用备份恢复。
这项用例证明失败不会清理唯一旧版本。它尚未覆盖写到一半失败和 fsync失败,需要可注入文件适配层或平台故障工具。
大纲服务在 ArkTS 运行时验证
describe('Markdown 大纲', () => {
it('设备端提取标题与 UTF-16 跳转偏移',
0,
() => {
const content =
'# 鸿蒙 PC\n\n正文\n---\n\n' +
'```md\n## 代码标题\n```\n\n' +
'### 目标标题';
const headings =
extractMarkdownHeadings(content);
expect(headings.length).assertEqual(3);
expect(headings[2].offset).assertEqual(
content.indexOf('### 目标标题')
);
}
);
});
无需文件 I/O的纯函数也值得在 ArkTS测试,确认目标运行时正则和字符串索引。中文让 UTF-8字节方案无法误通过,代码围栏验证状态机过滤。
UnitTestBuild 与设备执行不同
Hvigor UnitTestBuild验证测试代码能够编译打包,不等于用例已在设备运行。质量记录分别写:UnitTestBuild成功;ohosTest安装并在 MateBook Pro 2in1执行4/4成功。
如果没有连接目标,只能宣称构建成功。测试报告中把“设备执行”与“调用链审查”分开,防止质量数字虚高。
鸿蒙 PC 测试后的应用
下图为设备测试使用的文档格式版本运行在模拟器中。ohosTest本身输出在测试运行器,应用截图证明同一服务进入真实 HAP。

理想证据还应保存测试运行器结果截图或结构化报告,但每篇文章至少包含应用内部画面。截图不替代4/4日志。
测试结果读取
设备执行结束应记录目标名、应用版本、构建模式、用例数、失败堆栈和时间。HDC连接断开、安装失败与断言失败是不同状态,脚本不能把“没有结果”当成功。
自动提取可解析 Hypium报告并生成 Markdown测试报告。日志中不得写 fixture之外的用户正文和 URI。
可扩展的测试结构
后续应拆分 DocumentFormat.test、Recovery.test、Outline.test和 Workspace.test,List只注册。共享临时目录助手可减少清理重复,但不能隐藏故障步骤。
参数化矩阵适合 BOM×换行×末尾换行;故障用例保持独立名称。每项先构造、执行、按字节/状态断言、清理。
当前边界
现有设备用例只有4项;AtomicFile强杀、用户 picker URI、两千项目录、权限撤销、主题生命周期和多标签关闭尚未自动化。模拟器通过不等于真机 provider行为。
结语
ohosTest把文本保真从算法推断带到 HarmonyOS真实运行时:Core File Kit写入、字节读取、沙箱备份、故障恢复和 UTF-16偏移都有设备断言。测试辅助不复用被测序列化器,前后清理保证隔离。
鸿蒙 PC编辑器的质量不能只有浏览器测试。凡是文件和系统 API参与的承诺,都需要在目标平台产生可重复证据,并诚实区分编译通过与设备执行通过。
更多推荐




所有评论(0)