Test helpers for consumers testing ZenWebsocket integrations.
This module provides utilities to create mock WebSocket servers, simulate disconnections, inject messages, and assert on client behavior during tests.
Usage
defmodule MyApp.WebSocketTest do
use ExUnit.Case
alias ZenWebsocket.Testing
setup do
{:ok, server} = Testing.start_mock_server()
on_exit(fn -> Testing.stop_server(server) end)
{:ok, server: server}
end
test "handles messages", %{server: server} do
{:ok, client} = ZenWebsocket.Client.connect(server.url)
# Inject a message from server to client
Testing.inject_message(server, ~s({"type": "hello"}))
# Verify client sent expected message
assert Testing.assert_message_sent(server, ~s({"type": "ping"}), 1000)
end
test "handles disconnection", %{server: server} do
{:ok, client} = ZenWebsocket.Client.connect(server.url)
# Simulate server disconnect
Testing.simulate_disconnect(server, :going_away)
assert ZenWebsocket.Client.get_state(client) == :disconnected
end
endFunctions
start_mock_server/1- Start a mock WebSocket serverstop_server/1- Stop a mock server and clean up resourcessimulate_disconnect/2- Trigger a disconnect scenarioinject_message/2- Send a message from server to connected clientsassert_message_sent/3- Verify client sent an expected message
API Functions
| Function | Arity | Description | Param Kinds |
|---|---|---|---|
assert_message_sent | 3 | Assert that a client sent an expected message to the server. | server: value, expected: value, timeout_ms: value |
inject_message | 2 | Inject a message from the server to all connected clients. | server: value, message: value |
simulate_disconnect | 2 | Simulate a WebSocket disconnect from the server side. | server: value, reason: value |
stop_server | 1 | Stop a mock server and clean up all resources. | server: value |
start_mock_server | 1 | Start a mock WebSocket server for testing. | opts: value |
Summary
Functions
Asserts that a client sent an expected message to the server.
Injects a message from the server to all connected clients.
Simulates a WebSocket disconnect from the server side.
Starts a mock WebSocket server for testing.
Stops a mock server and cleans up all resources.
Types
@type disconnect_reason() :: :normal | :going_away | {:code, pos_integer()}
@type server() :: %{ pid: pid(), port: pos_integer(), url: String.t(), message_agent: pid() }
Functions
@spec assert_message_sent(server(), term(), pos_integer()) :: boolean()
Asserts that a client sent an expected message to the server.
This function polls the captured messages and checks if any match the expected pattern within the given timeout.
Pattern Matching
The expected parameter can be:
- A string for exact match
- A regex for pattern match
- A map for partial JSON match (decoded message must contain all keys/values)
- A function that returns true/false
Examples
# Exact string match
assert Testing.assert_message_sent(server, ~s({"type": "ping"}), 1000)
# Regex match
assert Testing.assert_message_sent(server, ~r/"type": *"ping"/, 1000)
# Partial map match (message must contain these keys)
assert Testing.assert_message_sent(server, %{"type" => "ping"}, 1000)
# Custom function
assert Testing.assert_message_sent(server, fn msg ->
case Jason.decode(msg) do
{:ok, %{"type" => "ping"}} -> true
_ -> false
end
end, 1000)
Injects a message from the server to all connected clients.
The message is sent as a text frame to all currently connected WebSocket clients.
Examples
# Send JSON message
Testing.inject_message(server, ~s({"type": "notification", "data": "hello"}))
# Send plain text
Testing.inject_message(server, "ping")
@spec simulate_disconnect(server(), disconnect_reason()) :: :ok
Simulates a WebSocket disconnect from the server side.
This is useful for testing client reconnection behavior and error handling.
Disconnect Reasons
:normal- Clean close (code 1000):going_away- Server shutting down (code 1001){:code, n}- Custom close code
Examples
# Normal close
Testing.simulate_disconnect(server, :normal)
# Server going away
Testing.simulate_disconnect(server, :going_away)
# Custom close code
Testing.simulate_disconnect(server, {:code, 1008})
Starts a mock WebSocket server for testing.
Options
:port- Port to listen on (default: 0, which assigns a random available port):protocol-:httpor:tls(default::http):handler- Custom frame handler function (default: echo handler)
Returns
{:ok, server} where server is a map containing:
:pid- Server process PID:port- Actual port the server is listening on:url- Full WebSocket URL for connecting:message_agent- Agent PID for message capture
Examples
{:ok, server} = Testing.start_mock_server()
{:ok, client} = ZenWebsocket.Client.connect(server.url)
# With custom port
{:ok, server} = Testing.start_mock_server(port: 9999)
# With TLS
{:ok, server} = Testing.start_mock_server(protocol: :tls)
@spec stop_server(server()) :: :ok
Stops a mock server and cleans up all resources.
This should be called in test teardown (e.g., on_exit callback).
Examples
setup do
{:ok, server} = Testing.start_mock_server()
on_exit(fn -> Testing.stop_server(server) end)
{:ok, server: server}
end