Message Stubbing
Message stub mappings define how WireMock responds to incoming messages on message-based channels like WebSockets. This page covers how to create, manage, and configure message stubs.
Creating Message Stubs
Section titled “Creating Message Stubs”Basic Stub
Section titled “Basic Stub”The simplest message stub responds to any message:
import static com.github.tomakehurst.wiremock.client.WireMock.*;
messageStubFor( message() .withName("Simple stub") .willTriggerActions( sendMessage("Hello!").onOriginatingChannel()));{ "name": "Simple stub", "trigger": { "type": "message" }, "actions": [ { "type": "send", "channelTarget": { "type": "originating" }, "message": { "body": { "data": "Hello!" } } } ]}Matching Message Content
Section titled “Matching Message Content”Use body matchers to match specific message content:
// Exact matchmessageStubFor( message() .withBody(equalTo("ping")) .willTriggerActions( sendMessage("pong").onOriginatingChannel()));
// Regex matchmessageStubFor( message() .withBody(matching("hello.*")) .willTriggerActions( sendMessage("hi there!").onOriginatingChannel()));
// JSON path matchmessageStubFor( message() .withBody(matchingJsonPath("$.action", equalTo("subscribe"))) .willTriggerActions( sendMessage("{\"status\": \"subscribed\"}") .onOriginatingChannel()));[ { "name": "Exact match stub", "trigger": { "type": "message", "message": { "body": { "equalTo": "ping" } } }, "actions": [ { "type": "send", "channelTarget": { "type": "originating" }, "message": { "body": { "data": "pong" } } } ] }, { "name": "Regex match stub", "trigger": { "type": "message", "message": { "body": { "matches": "hello.*" } } }, "actions": [ { "type": "send", "channelTarget": { "type": "originating" }, "message": { "body": { "data": "hi there!" } } } ] }, { "name": "JSON path match stub", "trigger": { "type": "message", "message": { "body": { "matchesJsonPath": { "expression": "$.action", "equalTo": "subscribe" } } } }, "actions": [ { "type": "send", "channelTarget": { "type": "originating" }, "message": { "body": { "data": "{\"status\": \"subscribed\"}" } } } ] }]Matching by Channel
Section titled “Matching by Channel”Restrict stubs to specific WebSocket endpoints:
import static com.github.tomakehurst.wiremock.matching.RequestPatternBuilder.newRequestPattern;
// Match specific URL pathmessageStubFor( message() .onWebsocketChannelFromRequestMatching("/my-endpoint") .withBody(equalTo("test")) .willTriggerActions( sendMessage("response").onOriginatingChannel()));
// Match URL patternmessageStubFor( message() .onWebsocketChannelFromRequestMatching( newRequestPattern().withUrl(urlPathMatching("/api/v[0-9]+/ws"))) .willTriggerActions( sendMessage("API response").onOriginatingChannel()));
// Match with headersmessageStubFor( message() .onWebsocketChannelFromRequestMatching( newRequestPattern() .withUrl("/secure-ws") .withHeader("Authorization", matching("Bearer .*"))) .willTriggerActions( sendMessage("Authenticated!").onOriginatingChannel()));{ "name": "Channel pattern stub", "trigger": { "type": "message", "channel": { "type": "websocket", "initiatingRequestPattern": { "urlPathPattern": "/api/v[0-9]+/ws", "headers": { "Authorization": { "matches": "Bearer .*" } } } }, "message": { "body": { "equalTo": "test" } } }, "actions": [ { "type": "send", "channelTarget": { "type": "originating" }, "message": { "body": { "data": "response" } } } ]}Stub Priority
Section titled “Stub Priority”When multiple stubs match an incoming message, the stub with the highest priority (lowest number) is selected. If priorities are equal, the most recently added stub takes precedence.
// Low priority (will match as fallback)messageStubFor( message() .withName("Fallback stub") .withPriority(10) .willTriggerActions( sendMessage("fallback response").onOriginatingChannel()));
// High priority (will match first)messageStubFor( message() .withName("High priority stub") .withPriority(1) .withBody(equalTo("important")) .willTriggerActions( sendMessage("priority response").onOriginatingChannel()));{ "name": "High priority stub", "priority": 1, "trigger": { "type": "message", "message": { "body": { "equalTo": "important" } } }, "actions": [ { "type": "send", "channelTarget": { "type": "originating" }, "message": { "body": { "data": "priority response" } } } ]}Multiple Actions
Section titled “Multiple Actions”A single stub can trigger multiple actions:
messageStubFor( message() .withName("Multi-action stub") .withBody(equalTo("trigger")) .willTriggerActions( sendMessage("response1").onOriginatingChannel(), sendMessage("response2").onOriginatingChannel()));{ "name": "Multi-action stub", "trigger": { "type": "message", "message": { "body": { "equalTo": "trigger" } } }, "actions": [ { "type": "send", "channelTarget": { "type": "originating" }, "message": { "body": { "data": "response1" } } }, { "type": "send", "channelTarget": { "type": "originating" }, "message": { "body": { "data": "response2" } } } ]}Broadcasting Messages
Section titled “Broadcasting Messages”Send messages to multiple channels matching a pattern:
import static com.github.tomakehurst.wiremock.matching.RequestPatternBuilder.newRequestPattern;
messageStubFor( message() .withName("Broadcast stub") .onWebsocketChannelFromRequestMatching("/control") .withBody(equalTo("notify-all")) .willTriggerActions( sendMessage("notification") .onChannelsMatching( newRequestPattern() .withUrl(urlPathMatching("/subscribers/.*")))));{ "name": "Broadcast stub", "trigger": { "type": "message", "channel": { "type": "websocket", "initiatingRequestPattern": { "url": "/control" } }, "message": { "body": { "equalTo": "notify-all" } } }, "actions": [ { "type": "send", "channelTarget": { "type": "request-initiated", "channelType": "websocket", "requestPattern": { "urlPathPattern": "/subscribers/.*" } }, "message": { "body": { "data": "notification" } } } ]}Message Body from File
Section titled “Message Body from File”Instead of specifying message content inline, you can load the body from a file in the __files directory. This is useful for large payloads or when you want to manage message content separately from stub definitions.
Text Messages from File
Section titled “Text Messages from File”import static com.github.tomakehurst.wiremock.client.WireMock.*;
// Load text message body from __files/responses/welcome.jsonmessageStubFor( message() .withName("File-based response") .withBody(equalTo("connect")) .willTriggerActions( sendMessage() .withBodyFromFile("responses/welcome.json") .onOriginatingChannel()));{ "name": "File-based response", "trigger": { "type": "message", "message": { "body": { "equalTo": "connect" } } }, "actions": [ { "type": "send", "channelTarget": { "type": "originating" }, "message": { "body": { "filePath": "responses/welcome.json" } } } ]}The file path is relative to the __files directory. For example, if your WireMock root directory contains __files/responses/welcome.json, use "filePath": "responses/welcome.json".
Binary Messages from File
Section titled “Binary Messages from File”For binary data (images, protobuf, etc.), specify the encoding as BINARY:
import static com.github.tomakehurst.wiremock.common.entity.BinaryEntityDefinition.aBinaryMessage;
// Load binary message body from __files/data/image.pngmessageStubFor( message() .withName("Binary file response") .withBody(equalTo("get-image")) .willTriggerActions( sendMessage() .toOriginatingChannel() .withMessage( aBinaryMessage() .withFilePath("data/image.png"))));{ "name": "Binary file response", "trigger": { "type": "message", "message": { "body": { "equalTo": "get-image" } } }, "actions": [ { "type": "send", "channelTarget": { "type": "originating" }, "message": { "body": { "encoding": "binary", "filePath": "data/image.png" } } } ]}Inline Binary Data
Section titled “Inline Binary Data”You can also specify binary data inline using byte arrays in Java or Base64 in JSON:
import static com.github.tomakehurst.wiremock.common.entity.BinaryEntityDefinition.aBinaryMessage;
byte[] binaryData = new byte[] { 0x01, 0x02, 0x03, 0x04 };
messageStubFor( message() .withName("Inline binary response") .willTriggerActions( sendMessage() .toOriginatingChannel() .withMessage( aBinaryMessage() .withBody(binaryData))));{ "name": "Inline binary response", "trigger": { "type": "message" }, "actions": [ { "type": "send", "channelTarget": { "type": "originating" }, "message": { "body": { "encoding": "binary", "data": "AQIDBA==" } } } ]}Response Templating
Section titled “Response Templating”Message responses support Handlebars templating, allowing dynamic content based on the incoming message or request data:
// Echo the incoming messagemessageStubFor( message() .withBody(matching(".*")) .willTriggerActions( sendMessage("You said: {{message.body}}") .onOriginatingChannel()));
// Use JSON path on incoming messagemessageStubFor( message() .withBody(matchingJsonPath("$.name")) .willTriggerActions( sendMessage("Hello {{jsonPath message.body '$.name'}}!") .onOriginatingChannel()));
// Use data from the initiating requestmessageStubFor( message() .onWebsocketChannelFromRequestMatching( newRequestPattern().withUrl(urlPathMatching("/users/.*"))) .willTriggerActions( sendMessage("Connected to: {{request.path}}") .onOriginatingChannel()));
// Use template helpersmessageStubFor( message() .willTriggerActions( sendMessage("ID: {{randomValue length=8 type='ALPHANUMERIC'}}") .onOriginatingChannel()));{ "name": "Templated response stub", "trigger": { "type": "message", "message": { "body": { "matchesJsonPath": "$.name" } } }, "actions": [ { "type": "send", "channelTarget": { "type": "originating" }, "message": { "body": { "data": "Hello {{jsonPath message.body '$.name'}}!" } } } ]}Available Template Variables
Section titled “Available Template Variables”One of the two following variables will be available in message action templates, depending how the stub was triggered:
| Variable | Description |
|---|---|
message.body | The body of the incoming message that triggered this action |
request | The HTTP request that triggered this message action, same as the model used in response templates |
All standard response templating helpers are available.
Stub Metadata
Section titled “Stub Metadata”Add metadata to stubs for organization and filtering:
import static com.github.tomakehurst.wiremock.common.Metadata.metadata;
messageStubFor( message() .withName("Categorized stub") .withMetadata(metadata() .attr("category", "important") .attr("version", "2")) .willTriggerActions( sendMessage("response").onOriginatingChannel()));
// Find stubs by metadataList<MessageStubMapping> found = findMessageStubsByMetadata( matchingJsonPath("$.category", equalTo("important")));
// Remove stubs by metadataremoveMessageStubsByMetadata( equalToJson("{ \"category\": \"test\" }"));{ "name": "Categorized stub", "metadata": { "category": "important", "version": "2" }, "trigger": { "type": "message" }, "actions": [ { "type": "send", "channelTarget": { "type": "originating" }, "message": { "body": { "data": "response" } } } ]}Managing Stubs
Section titled “Managing Stubs”Listing Stubs
Section titled “Listing Stubs”ListMessageStubMappingsResult result = listAllMessageStubMappings();List<MessageStubMapping> stubs = result.getMessageMappings();curl http://localhost:8080/__admin/message-mappingsRemoving Stubs
Section titled “Removing Stubs”// Remove by IDwireMockServer.removeMessageStubMapping(stubId);
// Remove all stubswireMockServer.resetMessageStubMappings();
// Remove by metadataremoveMessageStubsByMetadata( matchingJsonPath("$.toRemove", equalTo(true)));# Remove single stubcurl -X DELETE http://localhost:8080/__admin/message-mappings/{id}
# Remove all stubscurl -X DELETE http://localhost:8080/__admin/message-mappings
# Remove by metadatacurl -X POST http://localhost:8080/__admin/message-mappings/remove-by-metadata \ -d '{"matchesJsonPath": {"expression": "$.toRemove", "equalTo": true}}'Loading Stubs from Files
Section titled “Loading Stubs from Files”Message stubs can be loaded from JSON files in the messages directory (alongside the mappings directory for HTTP stubs).
Single Stub File
Section titled “Single Stub File”messages/echo-stub.json:
{ "name": "Echo stub", "id": "11111111-1111-1111-1111-111111111111", "trigger": { "type": "message", "channel": { "type": "websocket", "initiatingRequestPattern": { "urlPath": "/echo" } } }, "actions": [ { "type": "send", "channelTarget": { "type": "originating" }, "message": { "body": { "data": "{{message.body}}" } } } ]}Multiple Stubs File
Section titled “Multiple Stubs File”messages/chat-stubs.json:
{ "messageMappings": [ { "name": "Join stub", "trigger": { "type": "message", "message": { "body": { "matchesJsonPath": "$.action", "equalTo": "join" } } }, "actions": [ { "type": "send", "channelTarget": { "type": "originating" }, "message": { "body": { "data": "{\"status\": \"joined\"}" } } } ] }, { "name": "Leave stub", "trigger": { "type": "message", "message": { "body": { "matchesJsonPath": "$.action", "equalTo": "leave" } } }, "actions": [ { "type": "send", "channelTarget": { "type": "originating" }, "message": { "body": { "data": "{\"status\": \"left\"}" } } } ] } ]}Message Action Transformers
Section titled “Message Action Transformers”Custom transformers can modify message actions before they are executed, similar to HTTP response transformers.
public class PrefixingMessageActionTransformer implements MessageActionTransformer { @Override public MessageAction transform(MessageAction action, MessageActionContext context) { if (action instanceof SendMessageAction sendAction) { String originalBody = getBody(sendAction); return sendMessage("[PREFIX] " + originalBody).onOriginatingChannel(); } return action; }
@Override public String getName() { return "prefixing"; }}
// Register the transformerWireMockServer wm = new WireMockServer( wireMockConfig() .dynamicPort() .extensions(new PrefixingMessageActionTransformer()));Non-global transformers can be applied to specific stubs:
messageStubFor( message() .willTriggerActions( sendMessage("original") .withTransformer("my-transformer") .onOriginatingChannel()));{ "actions": [ { "type": "send", "transformers": ["my-transformer"], "channelTarget": { "type": "originating" }, "message": { "body": { "data": "original" } } } ]}HTTP-Triggered Message Stubs
Section titled “HTTP-Triggered Message Stubs”There are two ways to trigger message actions from HTTP activity: by matching a request pattern, or by referencing a specific HTTP stub by its ID.
Triggered by Request Pattern
Section titled “Triggered by Request Pattern”Message stubs can be triggered by HTTP requests matching a pattern, enabling scenarios where an HTTP API call causes a WebSocket notification:
import static com.github.tomakehurst.wiremock.client.WireMock.*;import static com.github.tomakehurst.wiremock.matching.RequestPatternBuilder.newRequestPattern;
// Send WebSocket message when HTTP endpoint is calledmessageStubFor( message() .withName("HTTP-triggered notification") .triggeredByHttpRequest( newRequestPattern() .withMethod(POST) .withUrl("/api/notify")) .willTriggerActions( sendMessage("New notification!") .onChannelsMatching( newRequestPattern().withUrl(urlPathMatching("/notifications/.*")))));{ "name": "HTTP-triggered notification", "trigger": { "type": "http-request", "requestPattern": { "method": "POST", "url": "/api/notify" } }, "actions": [ { "type": "send", "channelTarget": { "type": "request-initiated", "channelType": "websocket", "requestPattern": { "urlPathPattern": "/notifications/.*" } }, "message": { "body": { "data": "New notification!" } } } ]}Triggered by Stub ID
Section titled “Triggered by Stub ID”Alternatively, you can trigger message actions when a specific HTTP stub is matched by referencing its UUID. This approach is useful when you want to link message actions to existing HTTP stubs without duplicating request matching logic:
import static com.github.tomakehurst.wiremock.client.WireMock.*;import static com.github.tomakehurst.wiremock.matching.RequestPatternBuilder.newRequestPattern;import java.util.UUID;
// First, create an HTTP stub and capture its IDUUID orderId = UUID.randomUUID();stubFor(post("/api/orders") .withId(orderId) .willReturn(ok("{\"orderId\": \"12345\"}")));
// Then, create a message stub triggered by that HTTP stubmessageStubFor( message() .withName("Order notification") .triggeredByHttpStub(orderId) .willTriggerActions( sendMessage("{\"type\": \"order_created\", \"orderId\": \"12345\"}") .onChannelsMatching(newRequestPattern().withUrl("/order-updates"))));
// You can also use the string form of the UUIDmessageStubFor( message() .withName("Another notification") .triggeredByHttpStub("11111111-1111-1111-1111-111111111111") .willTriggerActions( sendMessage("triggered!").onOriginatingChannel()));{ "name": "Order notification", "trigger": { "type": "http-stub", "stubId": "11111111-1111-1111-1111-111111111111" }, "actions": [ { "type": "send", "channelTarget": { "type": "request-initiated", "channelType": "websocket", "requestPattern": { "url": "/order-updates" } }, "message": { "body": { "data": "{\"type\": \"order_created\", \"orderId\": \"12345\"}" } } } ]}This approach separates concerns—the HTTP stub handles the request/response cycle, while the message stub handles the side-effect of pushing notifications to WebSocket clients. When the HTTP stub is matched and served, the associated message stub’s actions are triggered.