如何用Sinon模拟d3.select.selectAll?测试d3代码遇filter报错
解决D3 Selection Mock中的
array.filter is not a function错误 问题核心分析
你遇到的错误根源在于当前mock的texts对象没有正确模拟D3 Selection的结构与方法。D3的selectAll('text')返回的并非普通对象,而是D3 Selection实例(类型为d3.Selection),它自带D3封装的filter方法,而非数组的原生filter。你的mock仅实现了attr方法,缺少filter方法,导致调用时抛出类型错误。
textsSelected的真实类型
textsSelected是D3的Selection对象,结构和你控制台看到的一致:
- 包含
_groups:一个二维数组,内层是匹配到的DOM元素(如text节点)的NodeList/数组 - 包含
_parents:存储选择的父元素 - 原型链上挂载D3的核心方法:
attr、filter、each、style等,且支持链式调用
修正后的Mock实现方案
要让mock代码正常运行,需要模拟D3 Selection的核心结构和filter方法,同时保留attr的逻辑。以下是修正后的代码:
// 定义模拟D3 Selection的原型方法,复用核心逻辑 const mockD3SelectionProto = { // 模拟attr方法,支持链式调用,同时更新元素的样式记录 attr: function(name, value) { if (name === 'font-family') { this._groups.forEach(group => { group.forEach(el => { el.style = el.style || {}; el.style['font-family'] = value; }); }); } return this; // D3方法默认返回自身,支持链式调用 }, // 模拟D3的filter方法:遍历_groups,应用过滤回调,返回新的Selection filter: function(callback) { const filteredGroups = this._groups.map(group => { // 把NodeList/数组转换成纯数组,方便过滤 return Array.from(group).filter((el, index) => callback(el, index, group)); }); // 返回新的mock Selection,继承相同原型 return { _groups: filteredGroups, _parents: this._parents, __proto__: mockD3SelectionProto }; } }; // 构造模拟的texts Selection,包含测试用的元素 const texts = { _groups: [ [ {id: "textID2#StatusChangeChart", textContent: " New", __data__: "hi" }, undefined, // 测试过滤用的undefined元素 123 // 测试过滤用的数值元素 ] ], _parents: [document.createElement('svg')], // 模拟父SVG元素 __proto__: mockD3SelectionProto }; // 修正后的Sinon Stub const d3SelectStub = sinon.stub(window.d3, 'select'); d3SelectStub.withArgs("#testUpdateImage").returns({ selectAll: function(selector) { if (selector === "text") { return texts; } // 其他选择器返回你原有的imageUpdateGroups return imageUpdateGroups; } });
关键改进点
- 链式调用支持:
attr方法返回this,符合D3的API设计,避免链式调用出错 - 实现
filter方法:完全模拟D3 Selection的过滤逻辑,返回新的mock Selection,确保原代码的filter调用正常执行 - 贴近真实结构:保留
_groups和_parents属性,和真实D3 Selection结构一致,减少模拟偏差
这样修改后,你的测试代码就能正常执行attr和filter操作,不会再抛出array.filter is not a function的错误。
内容的提问来源于stack exchange,提问作者Sharon
相关产品推荐
相关产品推荐

