Skip to content

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.

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()));

Use body matchers to match specific message content:

// Exact match
messageStubFor(
message()
.withBody(equalTo("ping"))
.willTriggerActions(
sendMessage("pong").onOriginatingChannel()));
// Regex match
messageStubFor(
message()
.withBody(matching("hello.*"))
.willTriggerActions(
sendMessage("hi there!").onOriginatingChannel()));
// JSON path match
messageStubFor(
message()
.withBody(matchingJsonPath("$.action", equalTo("subscribe")))
.willTriggerActions(
sendMessage("{\"status\": \"subscribed\"}")
.onOriginatingChannel()));

Restrict stubs to specific WebSocket endpoints:

import static com.github.tomakehurst.wiremock.matching.RequestPatternBuilder.newRequestPattern;
// Match specific URL path
messageStubFor(
message()
.onWebsocketChannelFromRequestMatching("/my-endpoint")
.withBody(equalTo("test"))
.willTriggerActions(
sendMessage("response").onOriginatingChannel()));
// Match URL pattern
messageStubFor(
message()
.onWebsocketChannelFromRequestMatching(
newRequestPattern().withUrl(urlPathMatching("/api/v[0-9]+/ws")))
.willTriggerActions(
sendMessage("API response").onOriginatingChannel()));
// Match with headers
messageStubFor(
message()
.onWebsocketChannelFromRequestMatching(
newRequestPattern()
.withUrl("/secure-ws")
.withHeader("Authorization", matching("Bearer .*")))
.willTriggerActions(
sendMessage("Authenticated!").onOriginatingChannel()));

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()));

A single stub can trigger multiple actions:

messageStubFor(
message()
.withName("Multi-action stub")
.withBody(equalTo("trigger"))
.willTriggerActions(
sendMessage("response1").onOriginatingChannel(),
sendMessage("response2").onOriginatingChannel()));

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/.*")))));

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.

import static com.github.tomakehurst.wiremock.client.WireMock.*;
// Load text message body from __files/responses/welcome.json
messageStubFor(
message()
.withName("File-based response")
.withBody(equalTo("connect"))
.willTriggerActions(
sendMessage()
.withBodyFromFile("responses/welcome.json")
.onOriginatingChannel()));

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".

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.png
messageStubFor(
message()
.withName("Binary file response")
.withBody(equalTo("get-image"))
.willTriggerActions(
sendMessage()
.toOriginatingChannel()
.withMessage(
aBinaryMessage()
.withFilePath("data/image.png"))));

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))));

Message responses support Handlebars templating, allowing dynamic content based on the incoming message or request data:

// Echo the incoming message
messageStubFor(
message()
.withBody(matching(".*"))
.willTriggerActions(
sendMessage("You said: {{message.body}}")
.onOriginatingChannel()));
// Use JSON path on incoming message
messageStubFor(
message()
.withBody(matchingJsonPath("$.name"))
.willTriggerActions(
sendMessage("Hello {{jsonPath message.body '$.name'}}!")
.onOriginatingChannel()));
// Use data from the initiating request
messageStubFor(
message()
.onWebsocketChannelFromRequestMatching(
newRequestPattern().withUrl(urlPathMatching("/users/.*")))
.willTriggerActions(
sendMessage("Connected to: {{request.path}}")
.onOriginatingChannel()));
// Use template helpers
messageStubFor(
message()
.willTriggerActions(
sendMessage("ID: {{randomValue length=8 type='ALPHANUMERIC'}}")
.onOriginatingChannel()));

One of the two following variables will be available in message action templates, depending how the stub was triggered:

VariableDescription
message.bodyThe body of the incoming message that triggered this action
requestThe HTTP request that triggered this message action, same as the model used in response templates

All standard response templating helpers are available.

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 metadata
List<MessageStubMapping> found = findMessageStubsByMetadata(
matchingJsonPath("$.category", equalTo("important")));
// Remove stubs by metadata
removeMessageStubsByMetadata(
equalToJson("{ \"category\": \"test\" }"));
ListMessageStubMappingsResult result = listAllMessageStubMappings();
List<MessageStubMapping> stubs = result.getMessageMappings();
// Remove by ID
wireMockServer.removeMessageStubMapping(stubId);
// Remove all stubs
wireMockServer.resetMessageStubMappings();
// Remove by metadata
removeMessageStubsByMetadata(
matchingJsonPath("$.toRemove", equalTo(true)));

Message stubs can be loaded from JSON files in the messages directory (alongside the mappings directory for HTTP stubs).

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}}" } }
}
]
}

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\"}" } }
}
]
}
]
}

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 transformer
WireMockServer 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()));

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.

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 called
messageStubFor(
message()
.withName("HTTP-triggered notification")
.triggeredByHttpRequest(
newRequestPattern()
.withMethod(POST)
.withUrl("/api/notify"))
.willTriggerActions(
sendMessage("New notification!")
.onChannelsMatching(
newRequestPattern().withUrl(urlPathMatching("/notifications/.*")))));

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 ID
UUID orderId = UUID.randomUUID();
stubFor(post("/api/orders")
.withId(orderId)
.willReturn(ok("{\"orderId\": \"12345\"}")));
// Then, create a message stub triggered by that HTTP stub
messageStubFor(
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 UUID
messageStubFor(
message()
.withName("Another notification")
.triggeredByHttpStub("11111111-1111-1111-1111-111111111111")
.willTriggerActions(
sendMessage("triggered!").onOriginatingChannel()));

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.