如何用node:assert断言Mock函数的指定调用参数?
扩展Node.js assert模块实现Mock函数参数断言
方案1:无需扩展,直接用现有API实现匹配检查
不用修改assert模块,结合assert.ok和数组的some方法就能实现类似Jest arrayContaining的效果:
const assert = require('node:assert'); it("should assert args", (t) => { const loggerSpy = t.mock.method(Logger, "info"); myFunctionUnderTest(); // 检查是否存在匹配参数的调用 let hasMatchingCall = false; for (const call of loggerSpy.mock.calls) { try { assert.deepStrictEqual(call.arguments, [2, "No consumer application for given code", "MODEL_ENERGY_FLOW"]); hasMatchingCall = true; break; } catch {} } assert.ok(hasMatchingCall, 'Logger.info未被传入预期参数调用'); });
这里通过遍历调用记录,用assert.deepStrictEqual做参数匹配,只要找到一次匹配就标记为通过,避免全量对比。
方案2:扩展assert模块添加自定义断言方法
如果想复用类似Jest的简洁语法,可以直接扩展node:assert的功能,添加专属断言方法:
1. 添加toHaveBeenCalledWith断言
针对Mock函数的参数验证,封装一个直接可用的断言方法:
const assert = require('node:assert'); // 给assert添加自定义断言方法 assert.toHaveBeenCalledWith = (spy, ...expectedArgs) => { const matched = spy.mock.calls.some(call => { try { assert.deepStrictEqual(call.arguments, expectedArgs); return true; } catch { return false; } }); if (!matched) { throw new assert.AssertionError({ message: `Spy未被传入预期参数调用:${JSON.stringify(expectedArgs)}`, actual: spy.mock.calls.map(call => call.arguments), expected: expectedArgs }); } };
使用时直接调用,语法和Jest几乎一致:
it("should assert args", (t) => { const loggerSpy = t.mock.method(Logger, "info"); myFunctionUnderTest(); assert.toHaveBeenCalledWith(loggerSpy, 2, "No consumer application for given code", "MODEL_ENERGY_FLOW"); });
2. 添加arrayContaining匹配器
如果需要更灵活的数组包含验证,可以实现类似Jest的arrayContaining:
const assert = require('node:assert'); const originalDeepStrictEqual = assert.deepStrictEqual; // 定义自定义匹配器标识 const ARRAY_CONTAINING_TAG = Symbol('ArrayContaining'); assert.arrayContaining = (expectedItems) => ({ [ARRAY_CONTAINING_TAG]: true, expectedItems }); // 重写deepStrictEqual以支持自定义匹配器 assert.deepStrictEqual = (actual, expected, message) => { if (expected?.[ARRAY_CONTAINING_TAG]) { const allMatched = expected.expectedItems.every(item => { return actual.some(actualItem => { try { originalDeepStrictEqual(actualItem, item); return true; } catch { return false; } }); }); if (!allMatched) { throw new assert.AssertionError({ message: message || '数组未包含所有预期元素', actual, expected: `arrayContaining(${JSON.stringify(expected.expectedItems)})` }); } } else { originalDeepStrictEqual(actual, expected, message); } };
使用方式和你期望的Jest语法一致:
it("should assert args", (t) => { const loggerSpy = t.mock.method(Logger, "info"); myFunctionUnderTest(); assert.deepStrictEqual(loggerSpy.mock.calls, assert.arrayContaining([ { arguments: [2, "No consumer application for given code", "MODEL_ENERGY_FLOW"] } ])); });
注意事项
- 扩展核心模块时,建议在项目测试入口文件中一次性完成定义,避免重复加载导致的冲突。
- 如果你使用的测试框架(比如AVA)支持自定义断言,也可以直接在框架的断言对象上扩展,比如给
t对象添加方法,会更贴合框架生态。
内容的提问来源于stack exchange,提问作者Frank
相关产品推荐
相关产品推荐

