MQTT Mocking
MQTT mocking enables testing of IoT devices, sensor networks, and real-time messaging systems without connecting to actual MQTT brokers. mockd provides a full-featured MQTT broker with configurable topics, authentication, QoS levels, and device simulation.
Overview
Section titled “Overview”Use MQTT mocks when you need to:
- Test IoT applications and device communication
- Simulate sensor networks and telemetry data
- Develop smart home or industrial automation systems
- Create reproducible test scenarios for MQTT clients
- Mock message queues and pub/sub patterns
- Test real-time notification systems
Quick Start
Section titled “Quick Start”Create a minimal MQTT broker mock:
version: "1.0"
mocks: - id: mqtt-broker name: Simple MQTT Broker type: mqtt enabled: true mqtt: port: 1883 topics: - topic: sensors/temperature qos: 1 messages: - payload: '{"value": 22.5, "unit": "celsius"}' repeat: true interval: "5s"Start the server and test:
# Start mockdmockd serve --config mockd.yaml
# Subscribe to messages using mockd CLImockd mqtt subscribe sensors/temperature
# Or use mosquitto clientmosquitto_sub -h localhost -p 1883 -t "sensors/#"Configuration
Section titled “Configuration”Full MQTT Spec
Section titled “Full MQTT Spec”version: "1.0"
mocks: - id: mqtt-full-example name: IoT MQTT Broker type: mqtt enabled: true mqtt: # Required: broker port port: 1883
# TLS configuration (optional) tls: enabled: true certFile: ./certs/server.crt keyFile: ./certs/server.key
# Authentication (optional) auth: enabled: true users: - username: device password: secret123 acl: - topic: "sensors/#" access: write - topic: "commands/#" access: read - username: admin password: admin123 acl: - topic: "#" access: readwrite
# Topic configurations topics: - topic: sensors/temperature qos: 1 retain: true messages: - payload: '{"value": 22.5}' interval: "5s" repeat: true
- topic: commands/+/execute qos: 2 onPublish: response: payload: '{"status": "executed"}' forward: responses/device
- topic: devices/{device_id}/telemetry qos: 0 deviceSimulation: enabled: true deviceCount: 10 deviceIdPattern: "device_{index}"Configuration Reference
Section titled “Configuration Reference”| Field | Type | Description |
|---|---|---|
port | integer | MQTT broker port (default: 1883) |
tls | object | TLS/SSL configuration |
auth | object | Authentication settings |
topics | array | Topic configurations |
Topics and Messages
Section titled “Topics and Messages”Topics define how the broker handles message publishing and subscription.
Basic Topic Configuration
Section titled “Basic Topic Configuration”topics: - topic: sensors/temperature qos: 1 retain: true messages: - payload: | { "deviceId": "temp_001", "value": 22.5, "unit": "celsius", "timestamp": "{{ now }}" } interval: "5s" repeat: trueTopic Fields
Section titled “Topic Fields”| Field | Type | Description |
|---|---|---|
topic | string | Topic pattern (supports wildcards) |
qos | integer | Quality of Service level (0, 1, or 2) |
retain | boolean | Retain last message for new subscribers |
messages | array | Predefined messages to publish |
onPublish | object | Handler for received messages |
deviceSimulation | object | Simulate multiple devices |
Topic Wildcards
Section titled “Topic Wildcards”MQTT supports two wildcard characters for topic subscriptions:
# Single-level wildcard (+)# Matches exactly one topic leveltopics: - topic: sensors/+/temperature # matches sensors/room1/temperature # matches sensors/room2/temperature
# Multi-level wildcard (#)# Matches any number of levels (must be last character)topics: - topic: sensors/# # matches sensors/temperature # matches sensors/humidity # matches sensors/room1/tempMessage Configuration
Section titled “Message Configuration”Define messages to publish automatically on a topic:
messages: - payload: '{"temperature": 22.5}' delay: "0s" # Initial delay before first publish repeat: true # Continuously publish at interval interval: "5s" # Time between repeated publishesMessage fields:
| Field | Type | Description |
|---|---|---|
payload | string | Message content (string or JSON) |
delay | duration | Initial delay before first publish |
repeat | boolean | Continuously publish at interval |
interval | duration | Time between repeated publishes |
Templated Payloads
Section titled “Templated Payloads”Use template expressions in message payloads:
messages: - payload: | { "deviceId": "sensor_001", "temperature": {{ random.int(18, 28) }}, "humidity": {{ random.int(40, 80) }}, "timestamp": "{{ now }}" } interval: "5s" repeat: trueMQTT templates use the same engine as HTTP responses — all 35 faker types, random functions, timestamps, and string functions are available. See the full list in the Response Templating guide.
Common templates for MQTT payloads:
| Template | Description |
|---|---|
{{ now }} | Current ISO timestamp |
{{ uuid }} | Random UUID |
{{ timestamp }} | Unix timestamp |
{{ random.int(min, max) }} | Random integer in range |
{{ random.float(min, max) }} | Random float in range |
{{ faker.name }} | Random person name |
{{ faker.ipv4 }} | Random IPv4 address |
{{ faker.latitude }} | Random latitude |
{{ faker.longitude }} | Random longitude |
Retained Messages
Section titled “Retained Messages”Retained messages are stored on the broker and delivered to new subscribers immediately:
topics: - topic: devices/status retain: true messages: - payload: '{"status": "online", "uptime": 12345}'When a client subscribes to devices/status, they immediately receive the last retained message.
QoS Levels
Section titled “QoS Levels”MQTT defines three Quality of Service levels for message delivery:
QoS 0 - At Most Once
Section titled “QoS 0 - At Most Once”Fire and forget. No acknowledgment required. Messages may be lost.
topics: - topic: sensors/motion qos: 0 messages: - payload: '{"detected": true}'Use for:
- High-frequency sensor data where occasional loss is acceptable
- Non-critical notifications
- Status updates that will be superseded
QoS 1 - At Least Once
Section titled “QoS 1 - At Least Once”Message delivered at least once. May have duplicates. Requires acknowledgment.
topics: - topic: sensors/temperature qos: 1 messages: - payload: '{"value": 22.5}'Use for:
- Important sensor readings
- Command acknowledgments
- Data that should not be lost
QoS 2 - Exactly Once
Section titled “QoS 2 - Exactly Once”Message delivered exactly once. Highest overhead with four-step handshake.
topics: - topic: commands/critical qos: 2 onPublish: response: payload: '{"status": "executed"}'Use for:
- Financial transactions
- Critical commands
- Messages where duplicates cause problems
Publish Handlers
Section titled “Publish Handlers”Respond to messages received on a topic using publish handlers.
Basic Response
Section titled “Basic Response”topics: - topic: commands/device/+ qos: 1 onPublish: response: payload: '{"status": "acknowledged", "timestamp": "{{ now }}"}' delay: "100ms"Forward Messages
Section titled “Forward Messages”Forward received messages to another topic:
topics: - topic: commands/device/+ qos: 1 onPublish: response: payload: '{"status": "acknowledged"}' forward: responses/deviceHandler Fields
Section titled “Handler Fields”| Field | Type | Description |
|---|---|---|
response | object | Message to publish in response |
response.payload | string | Response message content |
response.delay | duration | Delay before sending response |
forward | string | Topic to forward the message to |
Using Message Content in Responses
Section titled “Using Message Content in Responses”Reference the received message in your response:
topics: - topic: commands/+/execute qos: 1 onPublish: response: payload: | { "status": "executed", "command": "{{ message.payload }}", "executedAt": "{{ now }}" }Authentication
Section titled “Authentication”Configure username/password authentication with Access Control Lists (ACL).
Enable Authentication
Section titled “Enable Authentication”mqtt: port: 1883 auth: enabled: true users: - username: sensor-gateway password: gateway-secret acl: - topic: "sensors/#" access: readwrite - username: dashboard password: readonly-pass acl: - topic: "sensors/#" access: readUser Configuration
Section titled “User Configuration”| Field | Type | Description |
|---|---|---|
username | string | Login username |
password | string | Login password |
acl | array | Access control rules |
ACL Rules
Section titled “ACL Rules”| Field | Type | Description |
|---|---|---|
topic | string | Topic pattern (supports wildcards) |
access | string | Access level |
Access levels:
| Level | Description |
|---|---|
read / subscribe | Subscribe only |
write / publish | Publish only |
readwrite / all | Both subscribe and publish |
Role-Based Access Example
Section titled “Role-Based Access Example”auth: enabled: true users: # IoT devices - can only publish sensor data - username: device password: device123 acl: - topic: "sensors/#" access: write - topic: "commands/#" access: read
# Monitoring dashboard - read-only access - username: monitor password: monitor123 acl: - topic: "#" access: read
# Admin - full access - username: admin password: admin123 acl: - topic: "#" access: allTLS Configuration
Section titled “TLS Configuration”Enable TLS/SSL for encrypted MQTT connections.
Basic TLS Setup
Section titled “Basic TLS Setup”mqtt: port: 8883 tls: enabled: true certFile: ./certs/server.crt keyFile: ./certs/server.keyGenerate Self-Signed Certificates
Section titled “Generate Self-Signed Certificates”# Generate private keyopenssl genrsa -out server.key 2048
# Generate self-signed certificateopenssl req -new -x509 -key server.key -out server.crt -days 365
# Connect with TLSmosquitto_sub -h localhost -p 8883 --cafile server.crt -t "sensors/#"Device Simulation
Section titled “Device Simulation”Simulate multiple IoT devices publishing to topics.
Configuration
Section titled “Configuration”topics: - topic: devices/{device_id}/telemetry qos: 1 deviceSimulation: enabled: true deviceCount: 100 deviceIdPattern: "device_{index}"This creates 100 virtual devices (device_1 through device_100) each publishing to their own topic:
devices/device_1/telemetrydevices/device_2/telemetry- …
devices/device_100/telemetry
Device Simulation Fields
Section titled “Device Simulation Fields”| Field | Type | Description |
|---|---|---|
enabled | boolean | Enable device simulation |
deviceCount | integer | Number of virtual devices |
deviceIdPattern | string | Pattern for device IDs ({n}, {id}, or {index} replaced with number) |
Simulating Device Fleet
Section titled “Simulating Device Fleet”topics: # Temperature sensors - topic: sensors/temperature/{device_id} qos: 1 deviceSimulation: enabled: true deviceCount: 50 deviceIdPattern: "temp_sensor_{index}" messages: - payload: | { "deviceId": "{{deviceId}}", "temperature": {{ random.int(15, 35) }}, "timestamp": "{{ now }}" } interval: "10s" repeat: true
# Motion sensors - topic: sensors/motion/{device_id} qos: 0 deviceSimulation: enabled: true deviceCount: 20 deviceIdPattern: "motion_{index}"Examples
Section titled “Examples”IoT Sensor Network
Section titled “IoT Sensor Network”version: "1.0"
mocks: - id: iot-sensors name: IoT Sensor Network type: mqtt enabled: true mqtt: port: 1883
auth: enabled: true users: - username: sensor password: sensor123 acl: - topic: "sensors/#" access: write - username: gateway password: gateway123 acl: - topic: "#" access: all
topics: # Temperature sensor - topic: sensors/temperature qos: 1 retain: true messages: - payload: | { "deviceId": "temp_001", "type": "temperature", "value": {{ random.int(18, 28) }}, "unit": "celsius", "timestamp": "{{ now }}" } interval: "5s" repeat: true
# Humidity sensor - topic: sensors/humidity qos: 1 retain: true messages: - payload: | { "deviceId": "humid_001", "type": "humidity", "value": {{ random.int(40, 80) }}, "unit": "percent", "timestamp": "{{ now }}" } interval: "5s" repeat: true
# Motion sensor (less frequent) - topic: sensors/motion qos: 0 messages: - payload: | { "deviceId": "motion_001", "type": "motion", "detected": true, "zone": "entrance", "timestamp": "{{ now }}" } interval: "30s" repeat: true
# Device status (retained) - topic: devices/status qos: 1 retain: true messages: - payload: | { "deviceId": "gateway_001", "status": "online", "uptime": {{ random.int(1000, 50000) }}, "firmware": "1.2.3", "timestamp": "{{ now }}" } delay: "1s"Command and Response System
Section titled “Command and Response System”version: "1.0"
mocks: - id: command-system name: Device Command System type: mqtt enabled: true mqtt: port: 1883
topics: # Command topic - listens for commands and responds - topic: commands/device/+ qos: 2 onPublish: response: payload: | { "status": "acknowledged", "command": "{{ message.payload }}", "executedAt": "{{ now }}", "id": "{{ uuid }}" } delay: "50ms" forward: responses/device
# Response topic for monitoring - topic: responses/device qos: 1 retain: true
# Alert topic with high QoS - topic: alerts/+ qos: 2 retain: trueSmart Home System
Section titled “Smart Home System”version: "1.0"
mocks: - id: smart-home name: Smart Home Hub type: mqtt enabled: true mqtt: port: 1883
auth: enabled: true users: - username: homeassistant password: ha-secret acl: - topic: "#" access: all - username: light password: light123 acl: - topic: "home/lights/#" access: readwrite - username: thermostat password: thermo123 acl: - topic: "home/climate/#" access: readwrite
topics: # Living room light - topic: home/lights/living_room/state qos: 1 retain: true messages: - payload: '{"on": true, "brightness": 80, "color": "#ffffff"}'
# Living room light commands - topic: home/lights/living_room/set qos: 1 onPublish: response: payload: | { "on": true, "brightness": 80, "updated": "{{ now }}" } forward: home/lights/living_room/state
# Thermostat - topic: home/climate/thermostat/state qos: 1 retain: true messages: - payload: | { "current_temp": {{ random.int(18, 24) }}, "target_temp": 21, "mode": "heat", "humidity": {{ random.int(40, 60) }} } interval: "30s" repeat: true
# Door sensor - topic: home/security/front_door qos: 1 retain: true messages: - payload: '{"state": "closed", "battery": 95}'
# Motion sensor - topic: home/security/motion/hallway qos: 0 messages: - payload: | { "motion": true, "timestamp": "{{ now }}" } interval: "60s" repeat: trueNotification Service
Section titled “Notification Service”version: "1.0"
mocks: - id: notifications name: Push Notification Service type: mqtt enabled: true mqtt: port: 1883
topics: # User notifications - topic: notifications/user/+ qos: 1 messages: - payload: | { "id": "{{ uuid }}", "type": "info", "title": "System Update", "body": "New features are available", "timestamp": "{{ now }}" } interval: "30s" repeat: true
# Broadcast notifications - topic: notifications/broadcast qos: 1 retain: true messages: - payload: | { "id": "{{ uuid }}", "type": "announcement", "title": "Maintenance Notice", "body": "Scheduled maintenance at midnight", "priority": "high" } delay: "5s"
# Notification acknowledgments - topic: notifications/ack/+ qos: 1 onPublish: response: payload: '{"status": "received", "timestamp": "{{ now }}"}'CLI Commands
Section titled “CLI Commands”mockd provides CLI tools for creating MQTT mocks and interacting with MQTT brokers.
Add an MQTT Mock
Section titled “Add an MQTT Mock”Create MQTT mocks directly from the command line using mockd mqtt add:
# Simple topic with payloadmockd mqtt add --topic sensors/temperature \ --payload '{"value": 22.5, "unit": "celsius"}'
# With QoS levelmockd mqtt add --topic sensors/humidity \ --payload '{"value": 65}' \ --qos 1
# Custom MQTT portmockd mqtt add --topic alerts/critical \ --payload '{"level": "high", "message": "Temperature exceeded"}' \ --mqtt-port 1884Output:
Merged into mock: mqtt_1c8177012ffef553 Type: mqtt Added: - sensors/temperature Total topics: - sensors/temperatureMQTT mocks on the same port are automatically merged. Adding more topics appends to the existing broker:
mockd mqtt add --topic sensors/humidity \ --payload '{"value": 65}' --qos 1
# Output:# Merged into mock: mqtt_1c8177012ffef553# Type: mqtt# Added:# - sensors/humidity# Total topics:# - sensors/temperature# - sensors/humidityAdd Command Flags
Section titled “Add Command Flags”| Flag | Description |
|---|---|
--topic | MQTT topic name (required) |
--payload | Message payload (string or JSON) |
--qos | Quality of Service level: 0, 1, or 2 (default: 0) |
--mqtt-port | MQTT broker port (default: 1883) |
--admin-url | Admin API URL (default: http://localhost:4290) |
mqtt publish
Section titled “mqtt publish”Publish a message to an MQTT topic:
# Publish a simple messagemockd mqtt publish localhost:1883 sensors/temperature "25.5"
# Publish to a custom brokermockd mqtt publish mqtt.example.com:1883 sensors/temp "25.5"
# Publish with authenticationmockd mqtt publish -u user -P pass localhost:1883 sensors/temp "25.5"
# Publish with QoS 1 and retainmockd mqtt publish --qos 1 --retain localhost:1883 sensors/temp "25.5"
# Publish JSON payloadmockd mqtt publish localhost:1883 sensors/data '{"temp": 25.5, "humidity": 60}'
# Publish from filemockd mqtt publish localhost:1883 sensors/config @config.jsonFlags:
| Flag | Description |
|---|---|
-m, --message | Message to publish (alternative to positional arg) |
-u, --username | MQTT username |
-P, --password | MQTT password |
-q, --qos | QoS level 0, 1, or 2 (default: 0) |
-r, --retain | Retain message on broker |
mqtt subscribe
Section titled “mqtt subscribe”Subscribe to a topic and print received messages:
# Subscribe to a topicmockd mqtt subscribe localhost:1883 sensors/temperature
# Subscribe with wildcardmockd mqtt subscribe localhost:1883 "sensors/#"
# Subscribe to single-level wildcardmockd mqtt subscribe localhost:1883 "sensors/+/temperature"
# Receive only 5 messages then exitmockd mqtt subscribe -c 5 localhost:1883 sensors/temperature
# Subscribe with timeoutmockd mqtt subscribe -t 30s localhost:1883 sensors/temperature
# Subscribe with authenticationmockd mqtt subscribe -u user -P pass localhost:1883 sensors/temperature
# Subscribe with QoS 1mockd mqtt subscribe --qos 1 localhost:1883 sensors/temperatureFlags:
| Flag | Description |
|---|---|
-u, --username | MQTT username |
-P, --password | MQTT password |
-q, --qos | QoS level 0, 1, or 2 (default: 0) |
-c, --count | Number of messages to receive (0 = unlimited) |
-t, --timeout | Timeout duration (e.g., 30s, 5m) |
mqtt status
Section titled “mqtt status”Show MQTT broker status from the admin API:
# Default admin URLmockd mqtt status
# Custom admin URLmockd mqtt status --admin-url http://localhost:9091
# JSON outputmockd mqtt status --jsonFlags:
| Flag | Description |
|---|---|
--admin-url | Admin API base URL (default: http://localhost:4290) |
--json | Output in JSON format |
Testing
Section titled “Testing”Using mockd CLI
Section titled “Using mockd CLI”# Start servermockd serve --config mockd.yaml &
# Subscribe to all sensors in backgroundmockd mqtt subscribe localhost:1883 "sensors/#" &
# Publish test messagesmockd mqtt publish localhost:1883 sensors/temperature '{"value": 25.5}'mockd mqtt publish localhost:1883 sensors/humidity '{"value": 65}'
# Test command/responsemockd mqtt subscribe localhost:1883 responses/device &mockd mqtt publish localhost:1883 commands/device/001 '{"action": "restart"}'Using Mosquitto Clients
Section titled “Using Mosquitto Clients”Install mosquitto clients:
# Ubuntu/Debianapt install mosquitto-clients
# macOSbrew install mosquitto
# Subscribe to all sensorsmosquitto_sub -h localhost -p 1883 -t "sensors/#" -v
# Subscribe to specific topicmosquitto_sub -h localhost -p 1883 -t "sensors/temperature"
# Publish a messagemosquitto_pub -h localhost -p 1883 -t "sensors/temperature" -m '{"value": 25.5}'
# Publish with QoS 1mosquitto_pub -h localhost -p 1883 -t "sensors/temperature" -m '{"value": 25.5}' -q 1
# Subscribe with authenticationmosquitto_sub -h localhost -p 1883 -u device -P secret123 -t "sensors/#"
# Publish with retain flagmosquitto_pub -h localhost -p 1883 -t "status" -m "online" -rIntegration Tests (JavaScript)
Section titled “Integration Tests (JavaScript)”const mqtt = require('mqtt');
describe('MQTT Mock', () => { let client;
beforeEach((done) => { client = mqtt.connect('mqtt://localhost:1883'); client.on('connect', done); });
afterEach(() => { client.end(); });
test('receives temperature readings', (done) => { client.subscribe('sensors/temperature', (err) => { expect(err).toBeNull(); });
client.on('message', (topic, message) => { const data = JSON.parse(message.toString()); expect(topic).toBe('sensors/temperature'); expect(data).toHaveProperty('value'); expect(data).toHaveProperty('unit'); done(); }); });
test('publishes and receives message', (done) => { const testTopic = 'test/messages'; const testMessage = { id: 1, text: 'hello' };
client.subscribe(testTopic);
client.on('message', (topic, message) => { const data = JSON.parse(message.toString()); expect(data).toEqual(testMessage); done(); });
client.publish(testTopic, JSON.stringify(testMessage)); });});Integration Tests (Go)
Section titled “Integration Tests (Go)”package main
import ( "encoding/json" "testing" "time"
mqtt "github.com/eclipse/paho.mqtt.golang")
func TestMQTTMock(t *testing.T) { opts := mqtt.NewClientOptions(). AddBroker("tcp://localhost:1883"). SetClientID("test-client")
client := mqtt.NewClient(opts) if token := client.Connect(); token.Wait() && token.Error() != nil { t.Fatalf("Failed to connect: %v", token.Error()) } defer client.Disconnect(250)
// Subscribe and receive message received := make(chan []byte, 1) client.Subscribe("sensors/temperature", 1, func(c mqtt.Client, m mqtt.Message) { received <- m.Payload() })
select { case msg := <-received: var data map[string]interface{} if err := json.Unmarshal(msg, &data); err != nil { t.Fatalf("Failed to parse message: %v", err) } if _, ok := data["value"]; !ok { t.Error("Expected 'value' field in message") } case <-time.After(10 * time.Second): t.Fatal("Timeout waiting for message") }}Integration Tests (Python)
Section titled “Integration Tests (Python)”import pytestimport paho.mqtt.client as mqttimport jsonimport time
def test_mqtt_subscribe(): received = []
def on_message(client, userdata, msg): received.append(json.loads(msg.payload))
client = mqtt.Client() client.on_message = on_message client.connect("localhost", 1883) client.subscribe("sensors/temperature") client.loop_start()
# Wait for message time.sleep(6) client.loop_stop() client.disconnect()
assert len(received) > 0 assert "value" in received[0]
def test_mqtt_publish(): received = []
def on_message(client, userdata, msg): received.append(json.loads(msg.payload))
client = mqtt.Client() client.on_message = on_message client.connect("localhost", 1883) client.subscribe("test/topic") client.loop_start()
# Publish message client.publish("test/topic", json.dumps({"test": True})) time.sleep(1)
client.loop_stop() client.disconnect()
assert len(received) == 1 assert received[0]["test"] == TrueTesting with Authentication
Section titled “Testing with Authentication”# Start server with auth enabledmockd serve --config mockd-auth.yaml &
# Test valid credentialsmosquitto_sub -h localhost -p 1883 -u sensor -P sensor123 -t "sensors/#"
# Test invalid credentials (should fail)mosquitto_sub -h localhost -p 1883 -u invalid -P wrong -t "sensors/#"
# Test ACL (device user can only write to sensors)mosquitto_pub -h localhost -p 1883 -u device -P device123 -t "sensors/temp" -m "25"mosquitto_sub -h localhost -p 1883 -u device -P device123 -t "commands/#" # deniedTesting QoS Levels
Section titled “Testing QoS Levels”# QoS 0 - At most oncemosquitto_pub -h localhost -p 1883 -t "qos/test" -m "qos0" -q 0
# QoS 1 - At least oncemosquitto_pub -h localhost -p 1883 -t "qos/test" -m "qos1" -q 1
# QoS 2 - Exactly oncemosquitto_pub -h localhost -p 1883 -t "qos/test" -m "qos2" -q 2
# Subscribe with specific QoSmosquitto_sub -h localhost -p 1883 -t "qos/test" -q 2Next Steps
Section titled “Next Steps”- Response Templating - Dynamic response values
- WebSocket Mocking - Real-time bidirectional communication
- TLS/HTTPS - Secure connections