2. 使用断言库验证字符串处理函数
字符串反转函数与测试目标
本节针对一个纯函数 reverseString(s) 编写单元测试。函数约定:接收字符串,返回其字符顺序反转后的新字符串;不修改原字符串,不抛异常。测试目标是覆盖四类输入:空串、单字符、多字符、含特殊字符(空格、标点、数字、Unicode 字符)。
待测实现(示例,可直接运行):
function reverseString(s) {
return s.split('').reverse().join('');
}
该实现按 UTF-16 码元拆分,对普通 ASCII 与常见标点正确;对代理对(如 emoji)会拆散,因此测试用例中把 emoji 作为“已知局限”单独标注,而不是当作通过项。
测试用例设计与断言写法
使用 Node.js 内置 assert 模块配合测试框架(如 Mocha、Jest 或 Node 原生 node:test)编写。以下给出可直接运行的完整测试文件,采用 node:test + node:assert/strict,无需额外依赖。
// reverseString.test.js
const test = require('node:test');
const assert = require('node:assert/strict');
function reverseString(s) {
return s.split('').reverse().join('');
}
test('空串返回空串', () => {
assert.strictEqual(reverseString(''), '');
});
test('单字符返回自身', () => {
assert.strictEqual(reverseString('A'), 'A');
});
test('多字符反转顺序', () => {
assert.strictEqual(reverseString('hello'), 'olleh');
});
test('特殊字符(空格、标点、数字)逐字符反转', () => {
assert.strictEqual(reverseString('a b!1'), '1!b a');
});
test('回文字符串反转后不变', () => {
assert.strictEqual(reverseString('level'), 'level');
});
test('已知局限:emoji 代理对被拆散', () => {
// 该实现按 UTF-16 码元拆分,emoji 会被拆成两个无效码元
const result = reverseString('a😀b');
assert.notStrictEqual(result, 'b😀a');
});
运行方式:
node --test reverseString.test.js。前 5 个用例应全部通过;第 6 个用例用于记录已知局限,断言“结果不等于正确反转”,从而在实现升级为按码点反转后该用例会失败并提醒更新。
关键断言选择与判读
| 用例 | 输入 | 期望输出 | 断言方法 | 覆盖目的 |
|---|---|---|---|---|
| 空串 | "" | "" | strictEqual | 长度 0 边界,不抛异常 |
| 单字符 | "A" | "A" | strictEqual | 长度 1 边界,恒等性 |
| 多字符 | "hello" | "olleh" | strictEqual | 常规反转逻辑 |
| 特殊字符 | "a b!1" | "1!b a" | strictEqual | 空格、标点、数字不被过滤 |
| 回文 | "level" | "level" | strictEqual | 反转后与原串相等 |
| emoji 局限 | "a😀b" | 不等于 "b😀a" | notStrictEqual | 记录代理对拆分问题 |
不要用
assert.equal(宽松相等)替代 strictEqual,否则 "1" 与 1 可能被误判为相等,掩盖类型错误。字符串测试应始终使用严格相等断言。
可复现的验证闭环
- 将上述测试文件保存为
reverseString.test.js。 - 执行
node --test reverseString.test.js,观察 6 个用例的通过/失败状态。 - 预期结果:前 5 个用例通过,第 6 个用例通过(因为断言的是“不等于正确反转”)。
- 若把实现改为按码点反转(如
[...s].reverse().join('')),第 6 个用例会失败,提示需要更新该局限用例。