patterntypescriptModerate
Testing WebSocket connections: use a real WS server in tests to avoid over-mocking
Viewed 0 times
websocket testingws serverreal websocketsocket integration testws events
Problem
Unit tests mock the WebSocket constructor, but the mock does not emit
open, message, or close events in the correct sequence. Tests pass but the real WebSocket client breaks in integration because the event flow was wrong.Solution
Spin up a real
ws server in beforeAll on a random port. Connect the client under test to it. The server can send scripted messages and the test asserts on what the client emits. Teardown closes both client and server in afterAll.Why
A real WebSocket server exercises the actual protocol handshake, event ordering, and reconnection logic. It is fast (in-process), isolated (random port), and verifies the full client stack rather than a mock with incorrect event timing.
Gotchas
- Use
{ port: 0 }to let the OS assign a random port — avoids conflicts in parallel test runs - The server must close all connections before the process exits — leaking connections cause Jest to hang after tests
- Test reconnection logic by calling
server.close()mid-test and asserting the client reconnects - Binary WebSocket frames need Buffer handling — ensure your test assertions account for Buffer vs string comparison
Code Snippets
Real WebSocket server in Jest test
import { WebSocketServer, WebSocket as WS } from 'ws';
let server: WebSocketServer;
let serverUrl: string;
beforeAll(async () => {
await new Promise<void>((resolve) => {
server = new WebSocketServer({ port: 0 }, resolve);
});
const addr = server.address() as { port: number };
serverUrl = `ws://localhost:${addr.port}`;
});
afterAll(() => server.close());
it('receives a message from the server', async () => {
server.once('connection', (ws) => ws.send('hello'));
const msg = await new Promise<string>((resolve) => {
const client = new WS(serverUrl);
client.onmessage = (e) => { resolve(e.data as string); client.close(); };
});
expect(msg).toBe('hello');
});Revisions (0)
No revisions yet.