DEV Community

楊東霖
楊東霖

Posted on • Originally published at devplaybook.cc

WebSocket Testing Tools: Complete Guide (2025)

WebSockets enable real-time bidirectional communication — chat apps, live dashboards, collaborative editing, and push notifications all depend on them. But unlike REST APIs, WebSockets maintain persistent connections, which makes testing them fundamentally different.

This guide covers every tool and technique you need to test WebSocket connections, from browser DevTools to CLI clients to load testing.


Understanding the WebSocket Lifecycle

Before testing, understand what you're testing:

Client                          Server
  |                               |
  |---HTTP GET (Upgrade request)->|
  |<--101 Switching Protocols-----|
  |                               |
  |====WebSocket Connection=======|
  |                               |
  |---{ "type": "ping" }--------->|
  |<--{ "type": "pong" }----------|
  |                               |
  |---Connection Close----------->|
  |<--Connection Close------------|
Enter fullscreen mode Exit fullscreen mode

The WebSocket connection starts as an HTTP request with an Upgrade: websocket header. If the server accepts, it responds with 101 Switching Protocols and the WebSocket session begins.

What you'll test:

  1. Connection establishment: Can you connect?
  2. Message sending/receiving: Are messages delivered correctly?
  3. Message format: JSON, binary, text?
  4. Reconnection: Does the client reconnect on disconnect?
  5. Error handling: What happens when the server sends an error?
  6. Load: How many concurrent connections can the server handle?

1. Browser DevTools (Built-in)

Best for: Inspecting existing WebSocket connections in web apps

Chrome, Firefox, and Edge all support WebSocket inspection in the Network tab:

  1. Open DevTools → Network tab
  2. Filter by WS (WebSocket)
  3. Reload the page or trigger the WebSocket connection
  4. Click the WebSocket connection in the list
  5. Go to the Messages tab to see all frames

What you can see:

  • Initial HTTP Upgrade request and headers
  • All sent and received messages with timestamps
  • Message sizes
  • Binary vs text frames

Limitation: DevTools only shows connections initiated by the current page. You can't initiate new connections from here.


2. Browser Console

Best for: Quick ad-hoc WebSocket testing without any tools

Open any browser console and test a WebSocket directly:

// Connect to a WebSocket server
const ws = new WebSocket('wss://echo.websocket.events');

// Set up event handlers
ws.onopen = () => {
  console.log('Connected');
  ws.send('Hello, WebSocket!');
};

ws.onmessage = (event) => {
  console.log('Received:', event.data);
};

ws.onerror = (error) => {
  console.error('Error:', error);
};

ws.onclose = (event) => {
  console.log('Disconnected:', event.code, event.reason);
};
Enter fullscreen mode Exit fullscreen mode

Sending JSON messages:

ws.send(JSON.stringify({ type: 'subscribe', channel: 'updates' }));
Enter fullscreen mode Exit fullscreen mode

Sending messages at intervals:

const interval = setInterval(() => {
  if (ws.readyState === WebSocket.OPEN) {
    ws.send(JSON.stringify({ type: 'ping', timestamp: Date.now() }));
  } else {
    clearInterval(interval);
  }
}, 1000);
Enter fullscreen mode Exit fullscreen mode

Closing the connection:

ws.close(1000, 'Normal closure');
Enter fullscreen mode Exit fullscreen mode

3. websocat (CLI)

Best for: Terminal-based WebSocket testing and scripting

websocat is the WebSocket equivalent of netcat — a powerful CLI client.

Install:

# macOS
brew install websocat

# Linux (download binary)
wget https://github.com/vi/websocat/releases/latest/download/websocat.x86_64-unknown-linux-musl
chmod +x websocat.x86_64-unknown-linux-musl
sudo mv websocat.x86_64-unknown-linux-musl /usr/local/bin/websocat
Enter fullscreen mode Exit fullscreen mode

Basic usage:

# Connect and interact interactively
websocat wss://echo.websocket.events

# Type messages, see responses
Enter fullscreen mode Exit fullscreen mode

Pipe messages:

# Send a single message and exit
echo "Hello" | websocat wss://echo.websocket.events

# Send JSON
echo '{"type":"ping"}' | websocat wss://your-server.com/ws
Enter fullscreen mode Exit fullscreen mode

With authentication headers:

websocat -H "Authorization: Bearer YOUR_TOKEN" wss://api.example.com/ws
Enter fullscreen mode Exit fullscreen mode

Continuous message stream:

# Send a new message every second
while true; do
  echo '{"type":"heartbeat","ts":'$(date +%s)'}'
  sleep 1
done | websocat wss://api.example.com/ws
Enter fullscreen mode Exit fullscreen mode

Listen for N messages then exit:

websocat --no-close -n 5 wss://api.example.com/ws
Enter fullscreen mode Exit fullscreen mode

4. Postman

Best for: Teams already using Postman for REST API testing

Postman added WebSocket support in 2021. It provides a GUI for WebSocket testing alongside your existing REST collections.

To use:

  1. Click NewWebSocket Request
  2. Enter the WebSocket URL
  3. Add headers if needed (under the Headers tab)
  4. Click Connect
  5. Type messages in the Message input and press Send

Postman WebSocket features:

  • Save connections and messages to collections
  • Add authentication headers
  • Support for JSON and text messages
  • Message history per session

Limitation: WebSocket collections can't be automated in the same way as REST collections.


5. Insomnia

Best for: Developers using Insomnia for API testing

Insomnia supports WebSocket connections alongside REST and GraphQL:

  1. Create a new WebSocket request
  2. Set the URL, headers, and connection settings
  3. Connect and send messages interactively

Insomnia supports environment variables in WebSocket URLs, making it easy to switch between dev/staging/production.


6. wscat (Node.js CLI)

Best for: Node.js developers who want a familiar tool

# Install globally
npm install -g wscat

# Connect
wscat -c wss://echo.websocket.events

# With headers
wscat -c wss://api.example.com/ws \
  -H "Authorization: Bearer TOKEN"

# With subprotocols
wscat -c wss://api.example.com/ws \
  --subprotocol "chat,notifications"
Enter fullscreen mode Exit fullscreen mode

7. Testing WebSockets in Node.js Scripts

Best for: Automated tests and CI pipelines

Use the ws library for programmatic WebSocket testing:

const WebSocket = require('ws');

async function testWebSocket(url) {
  return new Promise((resolve, reject) => {
    const ws = new WebSocket(url);
    const messages = [];
    const timeout = setTimeout(() => {
      ws.close();
      reject(new Error('WebSocket timeout'));
    }, 5000);

    ws.on('open', () => {
      console.log('Connected to', url);
      ws.send(JSON.stringify({ type: 'ping' }));
    });

    ws.on('message', (data) => {
      const msg = JSON.parse(data.toString());
      messages.push(msg);

      if (msg.type === 'pong') {
        clearTimeout(timeout);
        ws.close();
        resolve(messages);
      }
    });

    ws.on('error', reject);
  });
}

// Run the test
testWebSocket('wss://your-server.com/ws')
  .then(messages => console.log('Test passed:', messages))
  .catch(err => console.error('Test failed:', err));
Enter fullscreen mode Exit fullscreen mode

Jest integration:

describe('WebSocket server', () => {
  let ws;

  beforeEach((done) => {
    ws = new WebSocket('ws://localhost:3001');
    ws.on('open', done);
  });

  afterEach(() => {
    ws.close();
  });

  test('responds to ping with pong', (done) => {
    ws.send(JSON.stringify({ type: 'ping' }));
    ws.on('message', (data) => {
      const msg = JSON.parse(data);
      expect(msg.type).toBe('pong');
      done();
    });
  });

  test('broadcasts messages to all clients', (done) => {
    const ws2 = new WebSocket('ws://localhost:3001');
    ws2.on('open', () => {
      ws.send(JSON.stringify({ type: 'broadcast', text: 'hello' }));
    });
    ws2.on('message', (data) => {
      const msg = JSON.parse(data);
      expect(msg.text).toBe('hello');
      ws2.close();
      done();
    });
  });
});
Enter fullscreen mode Exit fullscreen mode

Load Testing WebSockets

Using Artillery

# artillery-ws.yml
config:
  target: "wss://your-server.com"
  phases:
    - duration: 60
      arrivalRate: 10  # 10 new connections per second

scenarios:
  - engine: "ws"
    flow:
      - connect: "/ws"
      - send: '{"type": "subscribe", "channel": "updates"}'
      - think: 5
      - send: '{"type": "ping"}'
      - think: 10
Enter fullscreen mode Exit fullscreen mode
npm install -g artillery
artillery run artillery-ws.yml
Enter fullscreen mode Exit fullscreen mode

Using k6

// k6-ws-test.js
import ws from 'k6/ws';
import { check } from 'k6';

export const options = {
  vus: 100,         // 100 virtual users
  duration: '30s',
};

export default function () {
  const url = 'wss://your-server.com/ws';
  const response = ws.connect(url, {}, function (socket) {
    socket.on('open', () => {
      socket.send(JSON.stringify({ type: 'subscribe' }));
    });

    socket.on('message', (data) => {
      const msg = JSON.parse(data);
      check(msg, { 'message received': (m) => m.type !== undefined });
    });

    socket.setTimeout(() => socket.close(), 5000);
  });

  check(response, { 'status is 101': (r) => r && r.status === 101 });
}
Enter fullscreen mode Exit fullscreen mode

Common WebSocket Issues and Fixes

Connection Fails with 403

The server is rejecting the handshake. Common causes:

  • Missing or incorrect Origin header
  • Authentication required
  • CORS misconfiguration
# Check what the server expects
websocat -v wss://api.example.com/ws 2>&1 | head -30
Enter fullscreen mode Exit fullscreen mode

Messages Drop Under Load

Implement a message queue and acknowledgment system:

const pendingAcks = new Map();

function sendWithAck(ws, message) {
  const id = Date.now();
  const payload = { ...message, id };
  pendingAcks.set(id, payload);
  ws.send(JSON.stringify(payload));

  setTimeout(() => {
    if (pendingAcks.has(id)) {
      console.warn('Message not acknowledged, resending:', id);
      ws.send(JSON.stringify(payload));
    }
  }, 3000);
}
Enter fullscreen mode Exit fullscreen mode

Connection Dies After 30-60 Seconds

Load balancers and proxies close idle connections. Implement heartbeat:

const heartbeat = setInterval(() => {
  if (ws.readyState === WebSocket.OPEN) {
    ws.send(JSON.stringify({ type: 'ping' }));
  }
}, 25000);  // Every 25 seconds
Enter fullscreen mode Exit fullscreen mode

ECONNREFUSED in Tests

Your WebSocket server isn't starting before the tests run. Add a wait:

function waitForWebSocket(url, timeout = 5000) {
  return new Promise((resolve, reject) => {
    const start = Date.now();
    function attempt() {
      const ws = new WebSocket(url);
      ws.on('open', () => { ws.close(); resolve(); });
      ws.on('error', () => {
        if (Date.now() - start > timeout) reject(new Error('Timeout'));
        else setTimeout(attempt, 100);
      });
    }
    attempt();
  });
}
Enter fullscreen mode Exit fullscreen mode

Quick Testing Checklist

  • [ ] Can you establish a connection?
  • [ ] Does the server send a welcome/connected message?
  • [ ] Can you send a message and receive a response?
  • [ ] Does the server handle malformed JSON gracefully?
  • [ ] Does the server close the connection cleanly?
  • [ ] Does your client reconnect after a disconnect?
  • [ ] Does the connection survive a 30-second idle period (heartbeat working)?
  • [ ] Under load, are messages delivered without dropping?

Related Tools


Level Up Your Real-Time Stack

Building a production WebSocket service? The Full-Stack Boilerplate Collection includes a Node.js API starter with WebSocket support, Redis pub/sub integration, and reconnection handling already built in.


Level Up Your Dev Workflow

Found this useful? Explore DevPlaybook — cheat sheets, tool comparisons, and hands-on guides for modern developers.

🛒 Get the DevToolkit Starter Kit on Gumroad — 40+ browser-based dev tools, source code + deployment guide included.

Top comments (0)