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

使用Mocha测试Socket.io服务端客户端连接的异步问题排查

嘿,这种异步时序问题在Socket.io测试里太常见了!我之前踩过一模一样的坑,给你几个实用的排查和解决思路:

1. 别让测试断言跑在连接事件前面

Socket.io的客户端连接、服务端触发connection事件都是异步操作,如果你同步写断言,大概率会出现“断言执行时事件还没触发”的情况。

比如用Jest的话,把测试改成异步函数,先等待客户端确认连接成功,再做断言:

test('connection事件应该触发handleSocket', async () => {
  // 启动客户端
  const client = io('http://localhost:3000');
  
  // 先等客户端连上服务端
  await new Promise(resolve => client.on('connect', resolve));
  
  // 这时候再断言spy被调用了
  expect(handleSocketSpy).toHaveBeenCalled();
  
  // 别忘了断开连接清理资源
  client.disconnect();
});

如果用Mocha的话,可以用done回调,在handleSocket里或者客户端connect事件里调用done(),确保测试等待异步完成。

2. 确认服务端的监听早于客户端连接

有时候我们会在测试里先创建客户端,再启动服务端的Socket.io监听,这就会导致第一个连接事件被漏掉。一定要保证:

  • 在测试的beforeAll/beforeEach里先启动服务,并且注册好io.on('connection', handleSocket)
  • 之后再创建客户端发起连接

举个Mocha的例子:

let server;
let ioInstance;

before((done) => {
  const app = express();
  server = app.listen(3000, () => {
    ioInstance = require('socket.io')(server);
    // 先注册监听
    ioInstance.on('connection', handleSocket);
    done();
  });
});

beforeEach(() => {
  // 重置spy
  jest.clearAllMocks();
});

after((done) => {
  server.close(done);
});

it('should trigger handleSocket on client connect', (done) => {
  const client = io('http://localhost:3000');
  client.on('connect', () => {
    expect(handleSocketSpy).toHaveBeenCalled();
    client.disconnect();
    done();
  });
});

3. 给服务端的handleSocket加个测试钩子

如果上面的方法还是不行,可以在handleSocket里专门加个测试用的Promise,让测试明确等待这个函数执行完成:

// 服务端代码(测试环境下)
let connectionResolved;
function handleSocket(socket) {
  // 原来的业务逻辑
  console.log('connection触发了'); // 你的日志
  
  // 测试用的钩子
  if (process.env.NODE_ENV === 'test') {
    connectionResolved = Promise.resolve();
  }
}

// 测试代码
test('wait for handleSocket to execute', async () => {
  const client = io('http://localhost:3000');
  await new Promise(resolve => client.on('connect', resolve));
  await connectionResolved; // 等handleSocket执行完
  expect(handleSocketSpy).toHaveBeenCalled();
});

4. 避免用真实网络连接(可选)

如果只是想单元测试handleSocket的逻辑,完全可以模拟Socket.io的connection事件,不用启动真实服务:

test('test handleSocket logic directly', () => {
  const mockSocket = { /* 模拟socket的方法,比如on、emit */ };
  ioInstance.emit('connection', mockSocket);
  expect(handleSocketSpy).toHaveBeenCalledWith(mockSocket);
});

不过这种方式更偏向纯单元测试,如果你要测端到端的连接流程,还是推荐前面的异步等待方法。

总的来说,核心问题就是测试没有等待异步事件完成就执行了断言,只要把时序理顺,问题就能解决啦!

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.21 07:57:09