> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/grafana/k6/llms.txt
> Use this file to discover all available pages before exploring further.

# WebSocket Testing

> Test WebSocket connections and real-time communication with k6

k6 provides comprehensive support for testing WebSocket connections through the `k6/ws` module, enabling you to test real-time communication, bidirectional messaging, and WebSocket server performance.

## Basic WebSocket Connection

Connect to a WebSocket server and handle events:

```javascript theme={null}
import ws from "k6/ws";
import { check } from "k6";

export default function () {
  const url = "ws://echo.websocket.org";
  const params = { tags: { my_tag: "hello" } };

  const response = ws.connect(url, params, function (socket) {
    socket.on('open', function open() {
      console.log('connected');
      socket.send(Date.now());
    });

    socket.on('message', function incoming(data) {
      console.log(`Received: ${data}`);
    });

    socket.on('close', function close() {
      console.log('disconnected');
    });

    socket.on('error', function (e) {
      console.log('Error:', e.error());
    });

    // Close connection after 2 seconds
    socket.setTimeout(function () {
      console.log('2 seconds passed, closing the socket');
      socket.close();
    }, 2000);
  });

  check(response, { "status is 101": (r) => r && r.status === 101 });
}
```

<Note>
  The WebSocket connection blocks the VU execution until the socket is closed or an error occurs. This is by design to maintain state during the connection lifecycle.
</Note>

## WebSocket Events

The socket object emits several events you can listen to:

<Tabs>
  <Tab title="open">
    Triggered when the connection is established.

    ```javascript theme={null}
    socket.on('open', function open() {
      console.log('WebSocket connection opened');
      socket.send('Hello Server!');
    });
    ```
  </Tab>

  <Tab title="message">
    Triggered when a message is received from the server.

    ```javascript theme={null}
    socket.on('message', function incoming(data) {
      console.log(`Received message: ${data}`);
      // Echo back
      socket.send(data);
    });
    ```
  </Tab>

  <Tab title="close">
    Triggered when the connection is closed.

    ```javascript theme={null}
    socket.on('close', function close() {
      console.log('Connection closed');
    });
    ```
  </Tab>

  <Tab title="error">
    Triggered when an error occurs.

    ```javascript theme={null}
    socket.on('error', function (e) {
      if (e.error() != "websocket: close sent") {
        console.log('Unexpected error:', e.error());
      }
    });
    ```
  </Tab>

  <Tab title="ping/pong">
    Triggered when ping/pong frames are received.

    ```javascript theme={null}
    socket.on('ping', function () {
      console.log('PING received');
    });

    socket.on('pong', function () {
      console.log('PONG received');
    });
    ```
  </Tab>
</Tabs>

## Sending Messages

Send messages to the WebSocket server:

```javascript theme={null}
import ws from "k6/ws";

export default function () {
  const url = "ws://echo.websocket.org";

  ws.connect(url, function (socket) {
    socket.on('open', function () {
      // Send text message
      socket.send('Hello World!');

      // Send JSON data
      socket.send(JSON.stringify({
        type: 'message',
        payload: { user: 'test', content: 'Hello' }
      }));

      // Send binary data
      socket.send(new Uint8Array([1, 2, 3, 4]).buffer);
    });

    socket.on('message', function (data) {
      console.log('Echo:', data);
      socket.close();
    });
  });
}
```

## Ping/Pong and Keep-Alive

Implement keep-alive mechanisms using ping/pong frames:

```javascript theme={null}
import ws from "k6/ws";
import { check } from "k6";

export default function () {
  const url = "ws://echo.websocket.org";
  const params = { tags: { my_tag: "keepalive" } };

  const response = ws.connect(url, params, function (socket) {
    socket.on('open', function open() {
      console.log('connected');
      
      // Send ping every 1 second
      socket.setInterval(function timeout() {
        socket.ping();
        console.log("Pinging every 1sec");
      }, 1000);
    });

    socket.on('pong', function () {
      console.log('PONG received - connection alive');
    });

    socket.on('message', function incoming(data) {
      console.log(`Roundtrip time: ${Date.now() - data} ms`);
      
      // Send another message after 500ms
      socket.setTimeout(function timeout() {
        socket.send(Date.now());
      }, 500);
    });

    socket.on('close', function close() {
      console.log('disconnected');
    });

    // Close after 5 seconds
    socket.setTimeout(function () {
      socket.close();
    }, 5000);
  });

  check(response, { "status is 101": (r) => r && r.status === 101 });
}
```

## Multiple Event Handlers

You can register multiple handlers for the same event:

```javascript theme={null}
socket.on('pong', function () {
  console.log('First PONG handler');
});

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

// Both handlers will be called when a pong is received
```

## Connection Parameters

Configure WebSocket connections with various parameters:

```javascript theme={null}
import ws from "k6/ws";

export default function () {
  const url = "wss://secure.websocket.org";
  
  const params = {
    headers: {
      "Authorization": "Bearer token123",
      "X-Custom-Header": "value",
    },
    tags: {
      websocket_type: "secure",
      endpoint: "production",
    },
    jar: http.cookieJar(), // Use shared cookie jar
    compression: "deflate", // Enable compression
  };

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

### Available Parameters

| Parameter     | Type      | Description                             |
| ------------- | --------- | --------------------------------------- |
| `headers`     | Object    | Custom HTTP headers for the handshake   |
| `tags`        | Object    | Tags for metrics categorization         |
| `jar`         | CookieJar | Cookie jar for authentication           |
| `compression` | String    | Compression algorithm (e.g., "deflate") |

## Response Object

The `ws.connect()` function returns a response object with connection information:

```javascript theme={null}
const response = ws.connect(url, function (socket) {
  // ...
});

console.log('Status:', response.status);      // 101 for successful upgrade
console.log('URL:', response.url);            // WebSocket URL
console.log('Headers:', response.headers);    // Response headers
console.log('Body:', response.body);          // Response body (if any)
```

## Testing Scenarios

<CodeGroup>
  ```javascript Echo Server Test theme={null}
  import ws from "k6/ws";
  import { check } from "k6";
  import { Counter } from "k6/metrics";

  const messagesReceived = new Counter('messages_received');

  export default function () {
    const url = "ws://echo.websocket.org";
    const testMessages = ['Hello', 'World', 'Test'];
    let receivedCount = 0;

    ws.connect(url, function (socket) {
      socket.on('open', function () {
        testMessages.forEach(msg => socket.send(msg));
      });

      socket.on('message', function (data) {
        receivedCount++;
        messagesReceived.add(1);
        
        check(data, {
          'message echoed correctly': (d) => testMessages.includes(d),
        });

        if (receivedCount === testMessages.length) {
          socket.close();
        }
      });
    });
  }
  ```

  ```javascript Load Testing theme={null}
  import ws from "k6/ws";
  import { sleep } from "k6";

  export const options = {
    stages: [
      { duration: '30s', target: 50 },
      { duration: '1m', target: 100 },
      { duration: '30s', target: 0 },
    ],
  };

  export default function () {
    const url = "ws://your-websocket-server.com";

    ws.connect(url, function (socket) {
      socket.on('open', function () {
        // Send message every 2 seconds
        socket.setInterval(function () {
          socket.send(JSON.stringify({
            userId: __VU,
            iteration: __ITER,
            timestamp: Date.now(),
          }));
        }, 2000);
      });

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

      // Keep connection open for 30 seconds
      socket.setTimeout(function () {
        socket.close();
      }, 30000);
    });

    sleep(1);
  }
  ```
</CodeGroup>

## Error Handling

Properly handle errors and connection failures:

```javascript theme={null}
import ws from "k6/ws";
import { check } from "k6";

export default function () {
  const url = "ws://echo.websocket.org";

  const response = ws.connect(url, function (socket) {
    socket.on('error', function (e) {
      const errorMsg = e.error();
      
      // Ignore expected close errors
      if (errorMsg === "websocket: close sent") {
        return;
      }

      console.error('WebSocket error:', errorMsg);
      
      // Record error for analysis
      check(null, {
        'no unexpected errors': false,
      });
    });

    socket.on('close', function () {
      console.log('Connection closed gracefully');
    });

    // Test logic here
    socket.setTimeout(() => socket.close(), 2000);
  });

  // Verify successful connection
  check(response, {
    'connection established': (r) => r && r.status === 101,
    'no connection error': (r) => !r.error,
  });
}
```

<Warning>
  WebSocket connections in the init context are not supported. Always use WebSockets within the default function or other VU-scoped functions.
</Warning>

## Metrics

k6 automatically collects WebSocket metrics:

* `ws_connecting` - Time spent establishing the connection
* `ws_session_duration` - Total session duration
* `ws_msgs_sent` - Number of messages sent
* `ws_msgs_received` - Number of messages received
* `ws_ping` - Ping timing

## Best Practices

1. **Always close connections** explicitly to free up resources
2. **Handle all error cases** to ensure test reliability
3. **Use tags** to categorize different WebSocket endpoints
4. **Implement timeouts** to prevent infinite connections
5. **Monitor message rates** to avoid overwhelming the server
6. **Test reconnection logic** by intentionally closing connections

## Related Resources

* [WebSocket API Reference](/javascript-api/k6-ws)
* [Metrics Reference](/concepts/metrics)
* [Load Testing Guide](/test-types/load-testing)
