Skip to main content

ADVANCED

WebSocket Testing

Test real-time WebSocket connections with built-in support for text and binary messages, custom handlers, and seamless async event integration.

Why WebSocket Testing?

  • Real-time validation: Test live bidirectional communication between client and server
  • Built-in async handling: Integrated listen and signal patterns eliminate custom polling logic
  • Flexible filtering: Handler functions let you ignore noise and capture specific message patterns
  • Zero configuration: Works out of the box with automatic connection cleanup

Basic WebSocket Connection

Connect to a WebSocket endpoint and exchange messages:

Feature: Simple WebSocket

Scenario: Send and receive text message
* def socket = karate.webSocket('ws://echo.websocket.org')
* socket.send('Hello WebSocket')
* listen 5000
* match listenResult == 'Hello WebSocket'

Without a handler function, listen returns the first message received. The connection auto-closes when the scenario ends.

Message Handlers

Filter incoming messages with custom handler functions that return true to capture or false to ignore:

Feature: Filtered WebSocket messages

Scenario: Wait for specific message type
# Handler captures only messages starting with 'hello'
* def handler = function(msg) { return msg.startsWith('hello') }
* def socket = karate.webSocket('ws://localhost:8080/chat', handler)

# Send message
* socket.send('Billie')

# Wait for matching message (ignores others)
* listen 5000
* match listenResult == 'hello Billie !'
Handler Pattern

The handler function receives each incoming message. Return true to signal and populate listenResult, or false to keep waiting. Use handlers to filter out heartbeats, system messages, or irrelevant events.

WebSocket Options

Configure authentication, sub-protocols, and payload limits:

Feature: Authenticated WebSocket

Scenario: Connect with JWT authentication
* def token = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...'
* def options =
{
subProtocol: 'chat',
headers: { Authorization: 'Bearer ' + token },
maxPayloadSize: 8388608
}

* def socket = karate.webSocket('wss://api.example.com/socket', null, options)
* socket.send({ action: 'subscribe', channel: 'updates' })
* listen 5000
* def message = listenResult
* match message.channel == 'updates'

Available options:

  • subProtocol: Protocol negotiation string expected by server
  • headers: Authentication tokens, API keys, or custom headers
  • maxPayloadSize: Maximum message size in bytes (default: 4194304 / 4 MB)

Binary Messages

Handle binary WebSocket payloads with karate.webSocketBinary():

Feature: Binary WebSocket

Scenario: Send and receive binary data
* def binarySocket = karate.webSocketBinary('ws://localhost:8080/binary')

# Send byte array
* bytes payload = read('test-data.bin')
* binarySocket.send(payload)

# Receive binary response
* listen 5000
* bytes response = listenResult
* match response.length > 0

Use karate.webSocketBinary() for image uploads, file transfers, or any non-text protocol. The API signature matches karate.webSocket() but handles byte arrays instead of strings.

Listen and Signal Pattern

WebSocket integrates with Karate's async listen keyword for event-driven testing:

Feature: Async WebSocket workflow

Background:
* def wsUrl = 'ws://localhost:8080/events'

Scenario: Trigger action and wait for event
# Set up handler to capture completion events
* def handler =
"""
function(msg) {
var data = JSON.parse(msg);
return data.type === 'completed';
}
"""
* def socket = karate.webSocket(wsUrl, handler)

# Trigger async operation via HTTP
Given url baseUrl + '/process'
And request { orderId: 'ORD-123', priority: 'high' }
When method post
Then status 202

# Wait for WebSocket notification (max 10 seconds)
* listen 10000
* def event = JSON.parse(listenResult)
* match event == { type: 'completed', orderId: 'ORD-123', status: 'success' }

The listen keyword pauses execution until the handler returns true or timeout expires. If timeout occurs, listenResult is null.

Connection Cleanup

WebSocket connections auto-close at scenario end. No manual cleanup required. For long-running scenarios, close explicitly with socket.close() to free resources.

Real-Time Testing Patterns

Multi-Message Workflows

Test sequences of WebSocket interactions:

Feature: Chat workflow

Scenario: Join room and exchange messages
* def socket = karate.webSocket('ws://localhost:8080/chat')

# Join room
* socket.send({ action: 'join', room: 'lobby' })
* listen 3000
* match listenResult contains { type: 'joined', room: 'lobby' }

# Send message
* socket.send({ action: 'message', text: 'Hello everyone' })
* listen 3000
* match listenResult contains { type: 'message', text: 'Hello everyone' }

# Leave room
* socket.send({ action: 'leave' })
* listen 3000
* match listenResult.type == 'left'

Event Correlation

Correlate WebSocket events with HTTP operations:

Feature: Order processing pipeline

Scenario: Track order through WebSocket events
* def orderId = java.util.UUID.randomUUID() + ''
* def events = []

# Handler collects all events for this order
* def handler =
"""
function(msg) {
var event = JSON.parse(msg);
if (event.orderId === orderId) {
events.push(event);
return event.status === 'shipped'; # Stop at final status
}
return false;
}
"""
* def socket = karate.webSocket('ws://localhost:8080/orders', handler)

# Place order via HTTP
Given url baseUrl + '/orders'
And request { id: '#(orderId)', items: ['item1', 'item2'] }
When method post
Then status 201

# Wait for final event
* listen 15000
* match events == '#[3]' # Expect: created, processing, shipped
* match events[*].orderId == [orderId, orderId, orderId]
* match events[2].status == 'shipped'

When to Use WebSockets

Use WebSocket testing when:

  • Testing real-time features: notifications, live dashboards, chat applications
  • Validating bidirectional communication patterns
  • Testing event-driven architectures with WebSocket transports
  • Verifying server-sent events arrive correctly and in order

Prefer HTTP when:

  • Testing request-response APIs without real-time requirements
  • Communication is one-directional (client → server only)
  • Stateless operations that do not require persistent connections

Performance considerations:

  • WebSocket connections are lightweight but maintain server resources
  • Use realistic timeouts in listen to avoid hanging tests
  • Close connections explicitly in long-running scenarios
  • Test connection recovery and reconnection logic separately

Next Steps