You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用TypeScript+ws库时如何Mock WebSocket?(Jest环境)

针对ws库的Jest Mock最优方案

问题根源

你遇到的TypeError: ws_2.on is not a function,是因为mock-socket实现的WebSocket遵循浏览器标准(用addEventListener绑定事件),但Node.js的ws库使用自有API(on方法绑定事件),二者API不兼容导致的。

方案1:直接Mock ws库(无额外依赖,推荐)

不需要引入第三方mock库,直接用Jest原生mock功能模拟ws库核心API,完全匹配ws的调用方式。

步骤1:创建ws的Mock文件

在__tests__/mocks/ws.ts中编写如下代码:

export class WebSocket {
  private eventListeners: Record<string, Function[]> = {};
  public readyState: number = 0; // CONNECTING

  constructor(public url: string) {
    // 模拟连接成功,触发open事件
    process.nextTick(() => {
      this.readyState = 1; // OPEN
      this.emit('open');
    });
  }

  on(event: string, callback: Function) {
    if (!this.eventListeners[event]) {
      this.eventListeners[event] = [];
    }
    this.eventListeners[event].push(callback);
  }

  send(data: string) {
    // 模拟发送消息,可在此捕获内容用于断言
    this.emit('message', { data });
  }

  close() {
    this.readyState = 3; // CLOSED
    this.emit('close');
  }

  private emit(event: string, ...args: any[]) {
    this.eventListeners[event]?.forEach(callback => callback(...args));
  }
}

// 模拟ws库的其他导出(按需添加)
export const WebSocketServer = jest.fn();

步骤2:配置Jest使用Mock

在jest.config.ts中添加mock路径映射:

export default {
  // 其他配置...
  moduleNameMapper: {
    '^ws$': '<rootDir>/__tests__/mocks/ws.ts'
  }
};

步骤3:编写测试用例(MyClient.test.ts)

假设你的MyClient.ts是封装ws客户端的类,测试代码示例:

import { MyClient } from '../MyClient';
import { WebSocket } from 'ws';

// 类型断言,方便访问mock内部方法
const MockWebSocket = WebSocket as jest.MockedClass<typeof WebSocket>;

describe('MyClient', () => {
  beforeEach(() => {
    jest.clearAllMocks();
  });

  it('should connect and send message', async () => {
    const client = new MyClient('ws://localhost:8080');
    
    // 断言连接初始化
    expect(MockWebSocket).toHaveBeenCalledWith('ws://localhost:8080');
    expect(client.isConnected).toBe(true);

    // 模拟服务器推送消息
    const mockWsInstance = MockWebSocket.mock.instances[0];
    mockWsInstance.emit('message', { data: 'server response' });

    // 断言客户端处理消息
    expect(client.lastReceivedMessage).toBe('server response');

    // 测试发送消息
    client.sendMessage('hello server');
    expect(mockWsInstance.send).toHaveBeenCalledWith('hello server');
  });

  it('should handle connection close', () => {
    const client = new MyClient('ws://localhost:8080');
    const mockWsInstance = MockWebSocket.mock.instances[0];
    
    mockWsInstance.emit('close');
    expect(client.isConnected).toBe(false);
  });
});

示例客户端代码(MyClient.ts)

import { WebSocket } from 'ws';

export class MyClient {
  private ws: WebSocket | null = null;
  public isConnected = false;
  public lastReceivedMessage: string | null = null;

  constructor(url: string) {
    this.ws = new WebSocket(url);
    
    this.ws.on('open', () => {
      this.isConnected = true;
    });

    this.ws.on('message', (data) => {
      this.lastReceivedMessage = data.toString();
    });

    this.ws.on('close', () => {
      this.isConnected = false;
      this.ws = null;
    });
  }

  sendMessage(message: string) {
    if (this.ws && this.isConnected) {
      this.ws.send(message);
    }
  }
}

方案2:适配mock-socket的API

如果一定要用mock-socket,可以给它的WebSocket原型添加on方法,映射到addEventListener:

修改__tests__/mocks/ws.ts:

import { WebSocket as MockSocket } from 'mock-socket';

// 给mock-socket的WebSocket添加on方法,适配ws库API
MockSocket.prototype.on = function(event: string, callback: Function) {
  this.addEventListener(event, callback);
};

// 可选:添加once方法(如果客户端用到)
MockSocket.prototype.once = function(event: string, callback: Function) {
  const wrapper = (...args: any[]) => {
    callback(...args);
    this.removeEventListener(event, wrapper);
  };
  this.addEventListener(event, wrapper);
};

export { MockSocket as WebSocket };
export const WebSocketServer = jest.fn();

配置Jest的moduleNameMapper和方案1一致,测试用例可结合mock-socket的Server类模拟服务器:

import { Server } from 'mock-socket';
import { MyClient } from '../MyClient';

describe('MyClient', () => {
  let mockServer: Server;

  beforeEach(() => {
    mockServer = new Server('ws://localhost:8080');
  });

  afterEach(() => {
    mockServer.stop();
  });

  it('should receive message from server', (done) => {
    const client = new MyClient('ws://localhost:8080');
    
    mockServer.on('connection', (socket) => {
      socket.send('test message');
    });

    setTimeout(() => {
      expect(client.lastReceivedMessage).toBe('test message');
      done();
    }, 100);
  });
});

方案对比

  • 方案1:无额外依赖,自定义mock逻辑精准匹配ws库API,适合需要精细控制mock行为的场景。
  • 方案2:利用mock-socket现有功能,适配API后可快速模拟双向通信,适合完整WebSocket交互测试场景。

内容的提问来源于stack exchange,提问作者Kristi Jorgji

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 14:29:55