Skip to main content
Test WebSocket connections for real-time applications including chat, notifications, live updates, and streaming data.

WebSocket Testing Overview

WebSockets provide full-duplex communication over a single TCP connection. Unlike HTTP, WebSocket connections remain open, allowing servers to push data to clients. Common use cases:
  • Real-time chat applications
  • Live notifications
  • Collaborative editing
  • Gaming servers
  • Financial market data streams
  • IoT device communication

Basic WebSocket Example

This example tests the QuickPizza WebSocket API where multiple users connect and exchange messages:
import { randomString, randomIntBetween } from 'https://jslib.k6.io/k6-utils/1.2.0/index.js';
import ws from 'k6/ws';
import { check, sleep } from 'k6';

const sessionDuration = randomIntBetween(3000, 6000); // Session between 3-6 seconds

export const options = {
  vus: 10,
  iterations: 10,
};

export default function () {
  const url = `wss://quickpizza.grafana.com/ws`;
  const params = { tags: { my_tag: 'my ws session' } };
  const user = `user_${__VU}`;

  const res = ws.connect(url, params, function (socket) {
    socket.on('open', function open() {
      console.log(`VU ${__VU}: connected`);

      socket.send(JSON.stringify({ msg: 'Hello!', user: user }));

      socket.setInterval(function timeout() {
        socket.send(
          JSON.stringify({
            user: user,
            msg: `I'm saying ${randomString(5)}`,
            foo: 'bar',
          })
        );
      }, randomIntBetween(1000, 2000)); // Send message every 1-2 seconds
    });

    socket.on('ping', function () {
      console.log('PING!');
    });

    socket.on('pong', function () {
      console.log('PONG!');
    });

    socket.on('close', function () {
      console.log(`VU ${__VU}: disconnected`);
    });

    socket.on('message', function (message) {
      const data = JSON.parse(message);
      console.log(`VU ${__VU} received message: ${data.msg}`);
    });

    socket.setTimeout(function () {
      console.log(`VU ${__VU}: ${sessionDuration}ms passed, leaving the website`);
      socket.send(JSON.stringify({ msg: 'Goodbye!', user: user }));
    }, sessionDuration);

    socket.setTimeout(function () {
      console.log(`Closing the socket forcefully 3s after graceful LEAVE`);
      socket.close();
    }, sessionDuration + 3000);
  });

  check(res, { 'Connected successfully': (r) => r && r.status === 101 });
  sleep(1);
}
Status code 101 indicates “Switching Protocols” - the expected response when upgrading from HTTP to WebSocket.

WebSocket Event Handlers

k6 WebSocket API provides several event handlers:
Triggered when the connection is established:
socket.on('open', () => {
  console.log('Connected to WebSocket server');
  socket.send('Hello from k6!');
});

Advanced Patterns

Authenticated WebSocket Connection

Authenticate during setup and use token in WebSocket connection:
import ws from 'k6/ws';
import http from 'k6/http';
import { check } from 'k6';

export function setup() {
  const loginRes = http.post(
    'https://api.example.com/auth/login',
    JSON.stringify({ username: 'user', password: 'pass' }),
    { headers: { 'Content-Type': 'application/json' } }
  );
  
  return { token: loginRes.json('token') };
}

export default function (data) {
  const url = `wss://api.example.com/ws?token=${data.token}`;
  
  // Alternative: Pass token in headers
  const params = {
    headers: { 'Authorization': `Bearer ${data.token}` },
  };
  
  ws.connect(url, params, function (socket) {
    socket.on('open', () => {
      console.log('Authenticated connection established');
    });
    
    // ... handle messages
  });
}

Message Rate Control

Control how frequently messages are sent:
export default function () {
  const url = 'wss://api.example.com/ws';
  const messageInterval = 2000; // Send every 2 seconds
  const testDuration = 30000;   // Run for 30 seconds
  
  ws.connect(url, function (socket) {
    socket.on('open', () => {
      // Send messages at regular intervals
      socket.setInterval(() => {
        socket.send(JSON.stringify({
          type: 'ping',
          timestamp: Date.now(),
        }));
      }, messageInterval);
      
      // Close connection after duration
      socket.setTimeout(() => {
        socket.close();
      }, testDuration);
    });
  });
}

Response Validation

Validate server responses and track metrics:
import { Counter, Trend } from 'k6/metrics';

const messageCounter = new Counter('websocket_messages_received');
const messageLatency = new Trend('websocket_message_latency');

export default function () {
  const url = 'wss://api.example.com/ws';
  
  ws.connect(url, function (socket) {
    socket.on('open', () => {
      // Send message with timestamp
      socket.send(JSON.stringify({
        type: 'echo',
        timestamp: Date.now(),
      }));
    });
    
    socket.on('message', (data) => {
      messageCounter.add(1);
      
      const message = JSON.parse(data);
      const latency = Date.now() - message.timestamp;
      messageLatency.add(latency);
      
      check(message, {
        'response time under 1s': () => latency < 1000,
        'valid response type': (m) => m.type === 'echo',
      });
    });
  });
}

Load Testing Scenarios

Ramping VUs for WebSocket Connections

Gradually increase concurrent connections:
export const options = {
  scenarios: {
    websocket_load: {
      executor: 'ramping-vus',
      startVUs: 0,
      stages: [
        { duration: '30s', target: 50 },  // Ramp to 50 connections
        { duration: '1m', target: 50 },   // Stay at 50 connections
        { duration: '30s', target: 100 }, // Ramp to 100 connections
        { duration: '1m', target: 100 },  // Stay at 100 connections
        { duration: '30s', target: 0 },   // Ramp down to 0
      ],
    },
  },
};

export default function () {
  const url = 'wss://api.example.com/ws';
  
  ws.connect(url, function (socket) {
    socket.on('open', () => {
      // Simulate user activity for 5 minutes
      socket.setTimeout(() => {
        socket.close();
      }, 300000);
    });
  });
}

Multiple WebSocket Connections per VU

Simulate users with multiple concurrent WebSocket connections:
export default function () {
  const connections = 3; // Each VU opens 3 connections
  
  for (let i = 0; i < connections; i++) {
    ws.connect('wss://api.example.com/ws', function (socket) {
      socket.on('open', () => {
        console.log(`VU ${__VU}, connection ${i}: connected`);
      });
      
      // Keep connection alive for random duration
      socket.setTimeout(() => {
        socket.close();
      }, randomIntBetween(10000, 30000));
    });
  }
}
Each VU opens separate connections. With 10 VUs and 3 connections each, you’ll have 30 total WebSocket connections.

Best Practices

Always close connections gracefully:
socket.setTimeout(() => {
  // Send goodbye message
  socket.send(JSON.stringify({ type: 'disconnect' }));
  
  // Close connection after a brief delay
  socket.setTimeout(() => {
    socket.close();
  }, 1000);
}, sessionDuration);
Match production behavior:
// Bursty traffic (chat messages)
if (Math.random() < 0.1) { // 10% chance per second
  socket.send(JSON.stringify({ msg: 'Hello!' }));
}

// Regular heartbeat (IoT devices)
socket.setInterval(() => {
  socket.send(JSON.stringify({ type: 'heartbeat' }));
}, 60000); // Every minute
Track connection status and errors:
import { Rate } from 'k6/metrics';

const connectionErrors = new Rate('ws_connection_errors');

socket.on('error', (e) => {
  connectionErrors.add(1);
  console.error('Connection error:', e.error());
});

socket.on('open', () => {
  connectionErrors.add(0);
});
Define success criteria for WebSocket tests:
export const options = {
  thresholds: {
    'ws_connecting': ['p(95)<1000'],    // 95% connect under 1s
    'ws_msgs_sent': ['count>1000'],      // At least 1000 messages sent
    'ws_connection_errors': ['rate<0.1'], // Less than 10% errors
  },
};

Common Issues

Connection Refused

If connections fail, verify:
  • URL scheme is wss:// (secure) or ws:// (insecure)
  • Server accepts WebSocket upgrade requests
  • Authentication tokens are valid
  • Firewall rules allow WebSocket connections

High Memory Usage

For long-running tests:
  • Close connections properly
  • Don’t accumulate messages in memory
  • Use appropriate VU ramp-down periods

Timeout Issues

Adjust WebSocket timeout settings:
const params = {
  tags: { name: 'WebSocketTest' },
  timeout: '60s', // Increase timeout to 60 seconds
};

ws.connect(url, params, function (socket) {
  // ...
});

Custom Metrics

Track WebSocket-specific metrics

Test Scenarios

Create realistic load patterns

Data Generation

Generate realistic message payloads

API Reference: k6/ws

Complete WebSocket API documentation

Build docs developers (and LLMs) love