Mock Google Pub/Sub publish and pull in Python
A lightweight in-memory mock of Google Pub/Sub with publisher/subscriber classes to test topic-based fan-out and message pulling without real infrastructure.
Python code
69 linesimport json
import time
from collections import deque
from dataclasses import dataclass, field
from typing import Any, Callable
@dataclass
class Message:
data: str
attributes: dict[str, str] = field(default_factory=dict)
message_id: str | None = None
ack_id: str | None = None
class MockPublisher:
def __init__(self) -> None:
self._subscriptions: dict[str, deque[Message]] = {}
def subscribe(self, topic: str, subscription: str) -> None:
key = f"{topic}/{subscription}"
self._subscriptions.setdefault(key, deque())
def publish(self, topic: str, data: str, **attributes: Any) -> Message:
msg = Message(data=data, attributes=attributes)
for key in self._subscriptions:
if key.startswith(f"{topic}/"):
self._subscriptions[key].append(msg)
return msg
class MockSubscriber:
def __init__(self, publisher: MockPublisher) -> None:
self._publisher = publisher
def pull(self, topic: str, subscription: str, max_messages: int = 1) -> list[Message]:
key = f"{topic}/{subscription}"
queue = self._publisher._subscriptions.get(key)
if not queue:
return []
messages = []
for _ in range(min(max_messages, len(queue))):
msg = queue.popleft()
msg.ack_id = f"ack-{time.time()}-{len(messages)}"
messages.append(msg)
return messages
def acknowledge(self, msg: Message) -> None:
print(f"Acknowledged: {msg.ack_id}")
if __name__ == "__main__":
publisher = MockPublisher()
subscriber = MockSubscriber(publisher)
publisher.subscribe("orders", "order-processor")
publisher.subscribe("orders", "order-audit")
publisher.publish("orders", '{"order": 1}', source="api")
publisher.publish("orders", '{"order": 2}', source="cron")
pulled = subscriber.pull("orders", "order-processor", max_messages=2)
print(f"Pulled {len(pulled)} messages:")
for msg in pulled:
print(f" ID={msg.message_id} Data={msg.data} Attr={msg.attributes}")
subscriber.acknowledge(msg)
remaining = subscriber.pull("orders", "order-audit", max_messages=10)
print(f"Audit subscription remaining: {len(remaining)} messages")
Output
Pulled 2 messages:
ID=None Data={"order": 1} Attr={'source': 'api'}
Acknowledged: ack-...-0
ID=None Data={"order": 2} Attr={'source': 'cron'}
Acknowledged: ack-...-1
Audit subscription remaining: 2 messages
How it works
The MockPublisher stores messages in per-subscription deques keyed by topic and subscription name, so publish broadcasts to all matching subscriptions while pull pops messages FIFO from a single subscription. Each pulled message gets a unique ack_id, and acknowledge prints confirmation — mimicking the real Pub/Sub lifecycle. This mock lets you test fan-out and consumer behavior deterministically without a real GCP emulator.
Common mistakes
- Using a single queue for all subscriptions instead of keying by topic/subscription
- Not generating unique ack IDs, causing ambiguous acknowledgments
- Assuming publish is delivery-based rather than push-to-queue
- Forgetting to set a unique message_id for traceability
Variations
- Add a `delay` parameter to simulate network latency
- Implement subscription filtering or message ordering by timestamp
Real-world use cases
- Unit-testing event-driven services that read from a Pub/Sub subscription without spinning up an emulator.
- Replaying or load-testing consumer logic locally so bugs surface before deployment.
- Demonstrating fan-out to multiple subscribers in microservices workshops or CI demos.
Sponsored
More from Cloud + Python
- Build a URL Shortener Client with Python medium
- Create a Cloud Storage Helper Class in Python easy
- Create a Data Helper Class for Beginners in Python easy
- Cross Account Role Chaining Mock Credentials in Python medium
- Exponential Backoff with Jitter for Cloud API Calls in Python medium
- Generate Mock CloudFormation Stack Events in Python easy
Keep learning
Related tutorials and quizzes for this topic.