WebSocket Mocking
WebSocket mocking enables testing of real-time bidirectional communication without connecting to actual backend services. mockd provides full WebSocket support with message matching, response templating, and scripted scenarios.
Overview
Section titled “Overview”Use WebSocket mocks when you need to:
- Test chat applications, notifications, or live updates
- Simulate real-time data feeds (stock prices, sports scores, IoT sensors)
- Develop frontends before backend WebSocket services are ready
- Create reproducible test scenarios for bidirectional protocols
- Debug client-side WebSocket handling
Quick Start
Section titled “Quick Start”Create a minimal WebSocket mock:
version: "1.0"
mocks: - id: simple-ws name: Simple WebSocket type: websocket websocket: path: /ws echoMode: trueStart the server and connect:
# Start mockdmockd serve --config mockd.yaml
# Connect with the mockd CLImockd websocket connect ws://localhost:4280/ws
# Or use wscatwscat -c ws://localhost:4280/wsIn echo mode, any message you send is echoed back.
Configuration
Section titled “Configuration”Full WebSocket Spec
Section titled “Full WebSocket Spec”version: "1.0"
mocks: - id: ws-full-example name: Full WebSocket Example type: websocket enabled: true websocket: # Required: endpoint path path: /ws/chat
# Subprotocol negotiation subprotocols: - chat.v1 - chat.v2 requireSubprotocol: true # Reject connections without matching subprotocol
# Connection limits maxMessageSize: 65536 # Maximum message size in bytes maxConnections: 100 # Maximum concurrent connections idleTimeout: "5m" # Disconnect after inactivity
# Echo mode: reflect messages back to sender echoMode: false
# Heartbeat/keepalive heartbeat: enabled: true interval: "30s" # Send ping every 30 seconds timeout: "10s" # Disconnect if no pong within 10 seconds
# Message matchers (evaluated in order) matchers: - match: type: exact value: "ping" response: type: text value: "pong"
# Default response when no matcher matches defaultResponse: type: json value: error: "Unknown message"
# Scripted scenario (optional) scenario: name: welcome-flow steps: - type: send message: type: json value: { "type": "welcome" }Configuration Reference
Section titled “Configuration Reference”| Field | Type | Description |
|---|---|---|
path | string | Endpoint path (required) |
subprotocols | string[] | Supported WebSocket subprotocols |
requireSubprotocol | boolean | Reject connections without matching subprotocol |
maxMessageSize | integer | Maximum message size in bytes |
maxConnections | integer | Maximum concurrent connections |
idleTimeout | duration | Connection idle timeout (e.g., ”30s”, “5m”) |
echoMode | boolean | Echo received messages back to client |
heartbeat | object | Ping/pong keepalive configuration |
matchers | array | Message matchers with responses |
defaultResponse | object | Response when no matcher matches |
scenario | object | Scripted message sequence |
Message Matchers
Section titled “Message Matchers”Matchers define how to respond to incoming WebSocket messages. Each matcher has a match criteria and response configuration.
Match Types
Section titled “Match Types”Exact Match
Section titled “Exact Match”Match the entire message exactly:
matchers: - match: type: exact value: "ping" response: type: text value: "pong"Contains Match
Section titled “Contains Match”Match if the message contains a substring:
matchers: - match: type: contains value: "hello" response: type: text value: "Hello from mockd!"Prefix Match
Section titled “Prefix Match”Match if the message starts with a prefix:
matchers: - match: type: prefix value: "CMD:" response: type: text value: "Command received"Suffix Match
Section titled “Suffix Match”Match if the message ends with a suffix:
matchers: - match: type: suffix value: "?" response: type: text value: "That's a question!"Regex Match
Section titled “Regex Match”Match using a regular expression:
matchers: - match: type: regex value: "user_\\d+" response: type: json value: matched: true pattern: "user ID"JSON Path Match
Section titled “JSON Path Match”Match specific fields in JSON messages using JSONPath:
matchers: # Match messages where $.type equals "ping" - match: type: json path: "$.type" value: "ping" response: type: json value: type: "pong" timestamp: "{{now}}"
# Match messages where $.action equals "subscribe" - match: type: json path: "$.action" value: "subscribe" response: type: json value: action: "subscribed" channel: "{{message.channel}}"Message Type Filter
Section titled “Message Type Filter”Filter by WebSocket message type (text or binary):
matchers: - match: type: exact value: "ping" messageType: text # Only match text messages response: type: text value: "pong"
- match: type: regex value: ".*" messageType: binary # Only match binary messages response: type: text value: "Binary message received"No Response
Section titled “No Response”Match without sending a response (useful for logging or scenario progression):
matchers: - match: type: json path: "$.type" value: "heartbeat" noResponse: trueMatcher Priority
Section titled “Matcher Priority”Matchers are evaluated in order. The first matching rule wins:
matchers: # Specific match first - match: type: exact value: "ping" response: type: text value: "pong"
# Generic match later - match: type: regex value: ".*" response: type: text value: "Unknown command"Responses
Section titled “Responses”Response Types
Section titled “Response Types”Text Response
Section titled “Text Response”response: type: text value: "Hello, World!"JSON Response
Section titled “JSON Response”response: type: json value: status: "ok" timestamp: "{{now}}" data: message: "Welcome!"Binary Response
Section titled “Binary Response”response: type: binary value: "SGVsbG8gV29ybGQh" # Base64 encodedResponse Delay
Section titled “Response Delay”Add artificial latency:
response: type: json value: status: "processed" delay: "500ms"Templated Responses
Section titled “Templated Responses”Use template expressions in responses:
response: type: json value: id: "{{uuid}}" timestamp: "{{now}}" echo: "{{message}}" # Echo the received messageAvailable template variables:
| Expression | Description |
|---|---|
{{message}} | The received message content |
{{now}} | Current ISO timestamp |
{{timestamp}} | Unix timestamp (seconds) |
{{uuid}} | Random UUID |
Default Response
Section titled “Default Response”Define a fallback for unmatched messages:
websocket: path: /ws matchers: - match: type: exact value: "ping" response: type: text value: "pong"
defaultResponse: type: json value: type: "error" message: "Unknown command" received: "{{message}}"Echo Mode
Section titled “Echo Mode”When echoMode: true, messages are echoed back to the client. This is useful for testing client message handling:
websocket: path: /ws/echo echoMode: trueEcho mode works alongside matchers. Matchers are checked first; if none match and no default response is configured, the message is echoed.
Scenarios
Section titled “Scenarios”Scenarios enable scripted message sequences for complex testing flows.
Basic Scenario
Section titled “Basic Scenario”websocket: path: /ws/onboarding scenario: name: welcome-flow steps: # Send welcome message immediately on connect - type: send message: type: json value: type: "welcome" message: "Connected to server"
# Wait for client ready signal - type: wait match: type: json path: "$.type" value: "ready" timeout: "10s"
# Send session info - type: send message: type: json value: type: "session_start" sessionId: "{{uuid}}"Scenario Step Types
Section titled “Scenario Step Types”Send Step
Section titled “Send Step”Send a message to the client:
- type: send message: type: json value: event: "notification" data: "Hello!"Wait Step
Section titled “Wait Step”Wait for a client message:
- type: wait match: type: json path: "$.type" value: "acknowledge" timeout: "5s" optional: false # Fail if timeout expires (default)Delay Step
Section titled “Delay Step”Pause for a duration:
- type: delay duration: "2s"Looping Scenarios
Section titled “Looping Scenarios”Repeat the scenario when it completes:
scenario: name: heartbeat-loop loop: true steps: - type: delay duration: "5s" - type: send message: type: json value: type: "heartbeat" timestamp: "{{now}}"Reset on Reconnect
Section titled “Reset on Reconnect”Reset scenario state when a client reconnects:
scenario: name: tutorial resetOnReconnect: true steps: - type: send message: type: text value: "Welcome! Let's begin the tutorial..."Examples
Section titled “Examples”Chat Application
Section titled “Chat Application”version: "1.0"
mocks: - id: chat-room name: Chat Room type: websocket websocket: path: /ws/chat subprotocols: - chat - json maxMessageSize: 65536 idleTimeout: "10m" maxConnections: 100
heartbeat: enabled: true interval: "30s" timeout: "10s"
matchers: # Join room - match: type: json path: "$.type" value: "join" response: type: json value: type: "joined" message: "Welcome to the chat room!" timestamp: "{{now}}"
# Send message - match: type: json path: "$.type" value: "message" response: type: json value: type: "message_ack" id: "{{uuid}}" timestamp: "{{now}}"
# Leave room - match: type: json path: "$.type" value: "leave" response: type: json value: type: "left" message: "Goodbye!"
# Typing indicator - match: type: json path: "$.type" value: "typing" noResponse: true
# Status command - match: type: exact value: "status" response: type: json value: type: "status" users: 42 uptime: "{{timestamp}}"
# Help command - match: type: exact value: "help" response: type: text value: | Available commands: - {"type": "join", "username": "..."}: Join chat - {"type": "message", "content": "..."}: Send message - {"type": "leave"}: Leave chat - status: Get server status - help: Show this help
defaultResponse: type: json value: type: "error" message: "Unknown command. Type 'help' for available commands."Notification Service
Section titled “Notification Service”version: "1.0"
mocks: - id: notifications name: Push Notifications type: websocket websocket: path: /ws/notifications heartbeat: enabled: true interval: "30s"
matchers: # Subscribe to channel - match: type: json path: "$.action" value: "subscribe" response: type: json value: action: "subscribed" channel: "{{message.channel}}"
# Unsubscribe - match: type: json path: "$.action" value: "unsubscribe" response: type: json value: action: "unsubscribed" channel: "{{message.channel}}"
# Send periodic notifications scenario: name: notification-stream loop: true steps: - type: delay duration: "10s" - type: send message: type: json value: type: "notification" id: "{{uuid}}" title: "New update available" body: "Check out the latest features" timestamp: "{{now}}"Real-Time Data Feed
Section titled “Real-Time Data Feed”version: "1.0"
mocks: - id: stock-ticker name: Stock Ticker type: websocket websocket: path: /ws/stocks maxConnections: 1000
matchers: # Subscribe to symbol - match: type: json path: "$.action" value: "subscribe" response: type: json value: action: "subscribed" symbol: "{{message.symbol}}" message: "You will receive updates for this symbol"
# Simulate price updates scenario: name: price-updates loop: true steps: - type: delay duration: "1s" - type: send message: type: json value: type: "price_update" symbol: "MOCK" price: 123.45 change: 1.23 volume: 1000000 timestamp: "{{now}}"GraphQL Subscriptions
Section titled “GraphQL Subscriptions”version: "1.0"
mocks: - id: graphql-ws name: GraphQL WebSocket type: websocket websocket: path: /graphql subprotocols: - graphql-ws - graphql-transport-ws requireSubprotocol: true
matchers: # Connection init - match: type: json path: "$.type" value: "connection_init" response: type: json value: type: "connection_ack"
# Subscribe - match: type: json path: "$.type" value: "subscribe" response: type: json value: type: "next" id: "{{message.id}}" payload: data: onMessage: id: "{{uuid}}" content: "Subscription active"
# Ping - match: type: json path: "$.type" value: "ping" response: type: json value: type: "pong"CLI Commands
Section titled “CLI Commands”mockd provides CLI tools for interacting with WebSocket endpoints.
websocket connect
Section titled “websocket connect”Start an interactive WebSocket session (REPL mode):
# Basic connectionmockd websocket connect ws://localhost:4280/ws
# With custom headersmockd websocket connect -H "Authorization:Bearer token" ws://localhost:4280/ws
# With subprotocolmockd websocket connect --subprotocol graphql-ws ws://localhost:4280/graphql
# JSON output formatmockd websocket connect --json ws://localhost:4280/wsFlags:
-H, --header- Custom headers (key:value), repeatable--subprotocol- WebSocket subprotocol-t, --timeout- Connection timeout (default: 30s)--json- Output messages in JSON format
websocket send
Section titled “websocket send”Send a single message and exit:
# Send text messagemockd websocket send ws://localhost:4280/ws "hello"
# Send JSON messagemockd websocket send ws://localhost:4280/ws '{"action":"ping"}'
# Send from filemockd websocket send ws://localhost:4280/ws @message.json
# With custom headersmockd websocket send -H "Authorization:Bearer token" ws://localhost:4280/ws "hello"Flags:
-H, --header- Custom headers (key:value), repeatable--subprotocol- WebSocket subprotocol-t, --timeout- Connection timeout (default: 30s)--json- Output result in JSON format
websocket listen
Section titled “websocket listen”Stream incoming messages:
# Listen indefinitelymockd websocket listen ws://localhost:4280/ws
# Listen for 10 messages then exitmockd websocket listen -n 10 ws://localhost:4280/ws
# JSON outputmockd websocket listen --json ws://localhost:4280/ws
# With headersmockd websocket listen -H "Authorization:Bearer token" ws://localhost:4280/wsFlags:
-H, --header- Custom headers (key:value), repeatable--subprotocol- WebSocket subprotocol-t, --timeout- Connection timeout (default: 30s)-n, --count- Number of messages to receive (0 = unlimited)--json- Output messages in JSON format
websocket status
Section titled “websocket status”Show WebSocket mock status from the admin API:
# Default admin URLmockd websocket status
# Custom admin URLmockd websocket status --admin-url http://localhost:9091
# JSON outputmockd websocket status --jsonFlags:
--admin-url- Admin API base URL (default: http://localhost:4290)--json- Output in JSON format
Testing WebSocket Mocks
Section titled “Testing WebSocket Mocks”Using mockd CLI
Section titled “Using mockd CLI”# Start servermockd serve --config mockd.yaml &
# Test echo modemockd websocket send ws://localhost:4280/ws/echo "test message"
# Interactive testingmockd websocket connect ws://localhost:4280/ws/chat> {"type": "join", "username": "testuser"}< {"type": "joined", "message": "Welcome to the chat room!", "timestamp": "..."}> help< Available commands: ...Using wscat
Section titled “Using wscat”# Install wscatnpm install -g wscat
# Connect and interactwscat -c ws://localhost:4280/ws/chat> ping< pong> {"type": "join", "username": "alice"}< {"type": "joined", "message": "Welcome to the chat room!", "timestamp": "..."}Using curl (for connection testing)
Section titled “Using curl (for connection testing)”# Verify WebSocket endpoint exists (returns 400 for non-upgrade requests)curl -i http://localhost:4280/ws
# Test with upgrade headerscurl -i -N \ -H "Connection: Upgrade" \ -H "Upgrade: websocket" \ -H "Sec-WebSocket-Version: 13" \ -H "Sec-WebSocket-Key: $(openssl rand -base64 16)" \ http://localhost:4280/wsUsing websocat
Section titled “Using websocat”# Install websocatcargo install websocat
# Simple connectionwebsocat ws://localhost:4280/ws
# One-shot messageecho "ping" | websocat ws://localhost:4280/ws
# With subprotocolwebsocat --protocol chat ws://localhost:4280/ws/chatIntegration Tests (JavaScript)
Section titled “Integration Tests (JavaScript)”const WebSocket = require('ws');
describe('WebSocket Mock', () => { let ws;
beforeEach((done) => { ws = new WebSocket('ws://localhost:4280/ws/chat'); ws.on('open', done); });
afterEach(() => { ws.close(); });
test('responds to ping with pong', (done) => { ws.on('message', (data) => { expect(data.toString()).toBe('pong'); done(); }); ws.send('ping'); });
test('handles JSON messages', (done) => { ws.on('message', (data) => { const response = JSON.parse(data.toString()); expect(response.type).toBe('joined'); expect(response.message).toContain('Welcome'); done(); }); ws.send(JSON.stringify({ type: 'join', username: 'testuser' })); });});Integration Tests (Go)
Section titled “Integration Tests (Go)”package main
import ( "testing" "github.com/gorilla/websocket")
func TestWebSocketMock(t *testing.T) { conn, _, err := websocket.DefaultDialer.Dial("ws://localhost:4280/ws/chat", nil) if err != nil { t.Fatalf("Failed to connect: %v", err) } defer conn.Close()
// Test ping/pong if err := conn.WriteMessage(websocket.TextMessage, []byte("ping")); err != nil { t.Fatalf("Failed to send: %v", err) }
_, msg, err := conn.ReadMessage() if err != nil { t.Fatalf("Failed to read: %v", err) }
if string(msg) != "pong" { t.Errorf("Expected 'pong', got '%s'", msg) }}Integration Tests (Python)
Section titled “Integration Tests (Python)”import pytestimport websocketimport json
def test_websocket_ping(): ws = websocket.create_connection("ws://localhost:4280/ws/chat") ws.send("ping") result = ws.recv() assert result == "pong" ws.close()
def test_websocket_json(): ws = websocket.create_connection("ws://localhost:4280/ws/chat") ws.send(json.dumps({"type": "join", "username": "testuser"})) result = json.loads(ws.recv()) assert result["type"] == "joined" assert "Welcome" in result["message"] ws.close()Next Steps
Section titled “Next Steps”- Response Templating - Dynamic response values
- Request Matching - HTTP request matching patterns
- Stateful Mocking - CRUD simulation with state