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.

Easy Python 3.9+ Aug 9, 2026 Streaming & messaging 14 views 0 copies

Python code

40 lines
Python 3.9+
import 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

stdout
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

  1. Use a `set` instead of a list to deduplicate QoS levels per topic
  2. 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

Run this sample

Open the browser IDE to tweak the example and see results without installing anything.

Open editor

More from Streaming & messaging

Related tutorials and quizzes for this topic.