如何用Sinon正确模拟websockets/ws?遇TypeError问题求助
如何用Sinon正确模拟websockets/ws对象以测试WebSocket事件处理逻辑?
问题背景
我有C#开发背景,习惯用Moq类库模拟接口并验证调用。现在尝试用Sinon模拟websockets/ws库,想在无需实际连接的情况下测试应用的WebSocket事件处理逻辑,但运行测试时遇到错误:
TypeError: Attempted to wrap undefined property on as function
测试代码示例:
const WebSocket = require('ws'); const sinon = require('sinon'); const webSocket = sinon.mock(WebSocket); webSocket.expects('on').withArgs(sinon.match.any, sinon.match.any); const subject = new MyClass(logger, webSocket);
应用中的调用代码:
this._webSocket.on("open", () => { this.onWebSocketOpen(); });
问题根源
你踩的坑其实是Sinon和Moq在模拟对象时的一个关键差异:你用sinon.mock(WebSocket)模拟的是WebSocket类本身,但on方法是WebSocket实例才有的属性,类本身并没有这个方法,所以Sinon会报错说找不到要包装的on属性。
正确的模拟方案
根据你的代码场景(把WebSocket实例传入MyClass),这里有两种最常用的正确做法:
1. 直接模拟WebSocket实例
这是最贴合你当前代码结构的方式——直接创建一个模拟的WebSocket实例,然后传给MyClass:
const sinon = require('sinon'); // 创建一个空的模拟对象,专门用来模拟WebSocket实例 const wsMock = sinon.mock(); // 给这个模拟对象添加对`on`方法的调用预期 wsMock.expects('on') .withArgs('open', sinon.match.func) // 验证参数:第一个是'open',第二个是函数 .once(); // 可以指定预期被调用的次数,让测试更严谨 // 把模拟实例传给你的MyClass const subject = new MyClass(logger, wsMock); // 执行完你的测试逻辑后,验证所有预期是否都被满足 wsMock.verify();
2. 用Stub替代Mock(更灵活的验证方式)
如果你不需要严格的调用顺序,只是想验证on方法是否被正确调用,用sinon.stub会更灵活:
const sinon = require('sinon'); // 创建一个stub对象,然后给它添加`on`方法的stub const wsStub = {}; wsStub.on = sinon.stub(); // 传入MyClass const subject = new MyClass(logger, wsStub); // 执行测试逻辑后,验证调用情况 sinon.assert.calledOnce(wsStub.on); // 验证on被调用了一次 sinon.assert.calledWith(wsStub.on, 'open', sinon.match.func); // 验证调用参数正确
额外场景:如果MyClass内部创建WebSocket实例
如果你的MyClass是自己内部通过new WebSocket(...)创建实例,而不是外部传入,那你需要模拟WebSocket的构造函数,让它返回你预先准备好的模拟实例:
const WebSocket = require('ws'); const sinon = require('sinon'); // 先创建一个模拟的实例 const wsInstanceMock = sinon.mock(); wsInstanceMock.expects('on').withArgs('open', sinon.match.func); // 存根WebSocket构造函数,让它返回我们的模拟实例 const wsConstructorStub = sinon.stub(global, 'WebSocket').returns(wsInstanceMock); // 初始化MyClass(它内部会调用new WebSocket()) const subject = new MyClass(logger); // 验证预期 wsInstanceMock.verify(); // 测试结束后别忘了清理存根,避免影响其他测试 wsConstructorStub.restore();
内容的提问来源于stack exchange,提问作者BnMcG
相关产品推荐
相关产品推荐

