How to Mock MQTT Topic Subscriptions with QoS in Python
Build a lightweight MQTT client mock that tracks topic subscriptions with QoS levels and simulates wildcard message delivery.
Python code
40 linesimport time
from collections import defaultdict
class MockMQTTClient:
def __init__(self):
self.subscriptions = defaultdict(list)
self.messages = []
def subscribe(self, topic, qos=0):
self.subscriptions[topic].append(qos)
print(f"Subscribed to '{topic}' with QoS {qos}")
def publish(self, topic, payload):
print(f"Publishing '{payload}' to '{topic}'")
for sub_topic, qos_list in self.subscriptions.items():
if sub_topic == topic or topic.startswith(sub_topic.rstrip('/') + '/'):
for qos in qos_list:
delivery = f"QoS {qos}: {payload}"
self.messages.append((topic, delivery))
print(f" Delivered: {delivery}")
def main():
client = MockMQTTClient()
# Subscribe to various topics with different QoS levels
client.subscribe("sensors/temperature", qos=1)
client.subscribe("sensors/humidity", qos=2)
client.subscribe("sensors/#", qos=0) # wildcard subscription
print("\n--- Publishing messages ---")
client.publish("sensors/temperature", "23.5°C")
client.publish("sensors/humidity", "45%")
client.publish("sensors/pressure", "1013 hPa")
print("\n--- Message log ---")
for idx, (topic, msg) in enumerate(client.messages, 1):
print(f"{idx}. Topic: {topic} | {msg}")
if __name__ == "__main__":
main()
Output
Subscribed to 'sensors/temperature' with QoS 1
Subscribed to 'sensors/humidity' with QoS 2
Subscribed to 'sensors/#' with QoS 0
--- Publishing messages ---
Publishing '23.5°C' to 'sensors/temperature'
Delivered: QoS 1: 23.5°C
Delivered: QoS 0: 23.5°C
Publishing '45%' to 'sensors/humidity'
Delivered: QoS 2: 45%
Delivered: QoS 0: 45%
Publishing '1013 hPa' to 'sensors/pressure'
Delivered: QoS 0: 1013 hPa
--- Message log ---
1. Topic: sensors/temperature | QoS 1: 23.5°C
2. Topic: sensors/temperature | QoS 0: 23.5°C
3. Topic: sensors/humidity | QoS 2: 45%
4. Topic: sensors/humidity | QoS 0: 45%
5. Topic: sensors/pressure | QoS 0: 1013 hPa
How it works
The defaultdict(list) stores one QoS entry per subscription, allowing multiple QoS levels per topic. Wildcard matching uses startswith() on the stripped topic plus a slash, so sensors/# matches any subtopic. Each publish iterates subscriptions and delivers messages to matching topics, appending delivery records while printing them. The message log preserves delivery order, making it easy to verify QoS routing behavior in tests.
Common mistakes
- Using `in` instead of `startswith` for wildcard matching, which breaks hierarchical topics
- Not stripping trailing slashes before comparing topic hierarchies
- Ignoring duplicate delivery when the same topic matches both exact and wildcard subscriptions
Variations
- Use a `set` instead of a list to deduplicate QoS levels per topic
- Add a `retain` flag to store the last message per topic for late subscribers
Real-world use cases
- Unit-testing a home automation controller that processes temperature and humidity sensor feeds via MQTT.
- Simulating a fleet of IoT devices in a local dev environment without running a broker.
- Verifying QoS delivery guarantees in a data pipeline that ingests telemetry from multiple sensors.
Sponsored
More from Streaming & messaging
- At Most Once Fire-and-Forget Mock in Python easy
- Batch Consume Process Commit Pattern in Python medium
- Build a Streaming Messaging Helper in Python easy
- Dead Letter Queue Failed Messages List Mock in Python easy
- Dedupe processed message IDs in Python easy
- Event Envelope with Schema Version Field in Python easy
Keep learning
Related tutorials and quizzes for this topic.