wsmock

March 22, 2026 · View on GitHub

Go Reference Go Report Card License: MIT

wsmock is an expressive, zero-boilerplate WebSocket mock server for Go testing. It provides a declarative scenario builder and fault injection engine (network drops, slow responses, abnormal closure codes) to make testing WebSocket clients and services effortless.


Features

  • Zero Setup: Start an in-memory WebSocket mock server with one line (wsmock.NewServer(t)).
  • Declarative Expectations: Mock request-response patterns with ExpectMessage, ExpectJSON, or ExpectBinary.
  • Chaos and Fault Injection:
    • DropConnection(): Abruptly drops the underlying TCP socket without an RFC close frame (ideal for testing auto-reconnect logic).
    • CloseWithCode(code, reason): Sends RFC 6455 close frames (e.g., 1008 Policy Violation, 1011 Server Error).
    • Delay(d): Simulates network latency and slow responses.
  • Broadcast and Streaming: Push unprompted events to connected clients.
  • Recorded History and Assertions: Inspect and assert received payloads and expectation fulfillment.
  • Thread-Safe: Fully safe for concurrent test execution and tested with -race.

Installation

go get github.com/sing198/wsmock

Quick Start

1. Basic Request & Reply

func TestClientEcho(t *testing.T) {
    srv := wsmock.NewServer(t)

    // Set up expectation
    srv.ExpectMessage("ping").Reply("pong")

    // Connect client to srv.URL()
    conn, _, err := websocket.DefaultDialer.Dial(srv.URL(), nil)
    require.NoError(t, err)
    defer conn.Close()

    // Send and verify
    _ = conn.WriteMessage(websocket.TextMessage, []byte("ping"))
    _, reply, _ := conn.ReadMessage()
    assert.Equal(t, "pong", string(reply))

    // Assert that the expected message was received
    srv.AssertExpectationsMet()
}

2. Testing JSON Payloads

type AuthRequest struct {
    Token string `json:"token"`
}

type AuthResponse struct {
    Success bool `json:"success"`
}

func TestClientAuth(t *testing.T) {
    srv := wsmock.NewServer(t)

    srv.ExpectJSON(AuthRequest{Token: "secret"}).
        ReplyJSON(AuthResponse{Success: true})

    // ... dial and test your client ...
}

3. Fault Injection: Testing Reconnection Logic

Simulate abrupt network cuts without an RFC close frame to ensure reconnection clients recover gracefully:

func TestClientReconnectOnNetworkLoss(t *testing.T) {
    srv := wsmock.NewServer(t)

    // Abruptly sever the TCP connection upon receiving "trigger-drop"
    srv.ExpectMessage("trigger-drop").DropConnection()

    // Or close with specific close code:
    // srv.ExpectMessage("forbidden").CloseWithCode(websocket.ClosePolicyViolation, "denied")

    // Run client under test...
}

4. Broadcasting & Server Pushes

func TestServerStreaming(t *testing.T) {
    srv := wsmock.NewServer(t)

    // Broadcast messages to all active connections
    srv.Broadcast("ticker-update:100.5")

    // Or broadcast structured JSON
    srv.BroadcastJSON(MyEvent{Type: "price", Value: 100.5})
}

License

MIT © Thanaphat Khunphet