javascript数组高阶函数单元测试应验证行为契约而非实现细节,覆盖空数组、单元素、假值等边界,用test.each参数化多组输入,隔离副作用,确保纯函数特性。

针对 JavaScript 数组高阶函数(map、filter、reduce 等)编写完善的单元测试,核心在于:**验证行为契约而非实现细节,覆盖典型输入与边界,保持测试独立、可读、可维护**。它们本质是纯函数,天然适合单元测试——只要输入确定,输出就唯一,且无副作用。
聚焦函数签名与行为契约
每个高阶函数的测试应围绕其明确职责展开,不关心内部怎么遍历,只关心“给什么 → 得到什么”。例如:
-
map(fn):验证是否对每个元素调用fn,返回新数组,原数组不变 -
filter(predicate):验证是否仅保留使predicate返回真值的元素,顺序不变 -
reduce(reducer, init):验证是否按序累积,init是否作为首次调用的初始值
测试命名直接体现契约,如 test('map returns new array with transformed elements'),而不是 test('calls for loop inside map')。
覆盖典型输入与关键边界
数组操作的边界场景高度规律,必须显式覆盖:
- 空数组:
[]——map应返回[],reduce若无init应抛错 - 单元素数组:
[x]—— 验证函数是否被调用一次,结果是否正确 - 含假值元素:
[0, false, null, undefined, '']—— 尤其对filter,确认是否按布尔转换逻辑过滤 - 嵌套/复杂类型:
[[1], {a: 2}, () => {}]—— 验证函数能否安全处理非原始值
例如测试 filter 对假值的处理:
DMap GL地图JSAPI(dmapgl)开发指南。用于编写、审查、调试 DMap 地图 API 代码,涵盖地图初始化、覆盖物渲染、图层管理、事件处理、控件交互等任务。当用户提及 dmap、dmapgl、jsapi‑gl 或相关地图开发需求时自动触发。
test('filter keeps falsy values that satisfy predicate', () => {
const result = [0, false, '', null].filter(x => x === 0);
expect(result).toEqual([0]);
});
使用数据驱动统一验证多组输入
对同一函数的不同输入输出组合,优先用 test.each(Jest)或参数化方式组织,避免重复断言逻辑:
test.each([
[[1, 2, 3], x => x * 2, [2, 4, 6]],
[[], x => x + 1, []],
[[-1, 0, 1], Math.abs, [1, 0, 1]]
])('map(%j, %s) returns %j', (arr, fn, expected) => {
expect(arr.map(fn)).toEqual(expected);
});
新增测试用例只需加一行数据,不改结构,不易遗漏,也便于发现模式性错误。
隔离外部依赖,确保纯函数特性
即使测试的是原生 map,若你封装了带副作用的增强版(如带日志、异步等待),就必须 mock 外部影响:
- 避免在测试中依赖
Date.now()、Math.random()—— 用jest.useFakeTimers()或传入可控时间函数 - 若函数内调用了 API 或 DOM 方法,用
jest.fn()替换并断言调用次数与参数 - 对自定义 reducer,确保它不修改原对象(如用
structuredClone或immer测试不可变性)
例如测试一个“安全 reduce”,防止传入非函数 reducer:
test('reduce throws when reducer is not a function', () => {
expect(() => [1].reduce('not a function')).toThrow();
});
不复杂但容易忽略:真正完善的测试,不是跑满所有分支,而是让每个用例都像一句清晰的承诺——“当……时,它必须……”。这样,函数就是自解释的,测试就是活文档。










