使用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
相关产品推荐
相关产品推荐

