Observer Pattern with Mock Metrics in Python
Implement the Observer pattern with a mock metrics collector to track state changes and verify notifications.
Python code
53 linesimport unittest
from unittest.mock import Mock
class Subject:
def __init__(self):
self._state = 0
self._observers = []
def attach(self, observer):
self._observers.append(observer)
def set_state(self, value):
if value != self._state:
self._state = value
self._notify()
def _notify(self):
for observer in self._observers:
observer.update(self._state)
class ObserverMetrics:
def __init__(self):
self.update_calls = []
def update(self, state):
self.update_calls.append(state)
class TestSubjectObserverMetrics(unittest.TestCase):
def test_observer_notified_on_change(self):
subject = Subject()
metrics = ObserverMetrics()
subject.attach(metrics)
subject.set_state(1)
subject.set_state(2)
subject.set_state(2) # no change — no notification
self.assertEqual(metrics.update_calls, [1, 2])
if __name__ == "__main__":
subject = Subject()
mock_observer = Mock()
subject.attach(mock_observer)
subject.set_state(10)
subject.set_state(20)
called_states = [call.args[0] for call in mock_observer.update.call_args_list]
print(f"Observer notified with states: {called_states}")
Output
Observer notified with states: [10, 20]
How it works
The Subject maintains a list of observers and notifies them only when the state actually changes. The ObserverMetrics class collects update calls, allowing us to verify notifications in tests. The Mock in the main block records all update calls, letting us inspect the exact states passed. The equality check prevents redundant notifications for unchanged states. This pattern decouples state changes from downstream processing like metrics collection.
Common mistakes
- Notifying observers on every set_state call, even when the value doesn't change
- Forgetting to attach observers before setting state, so no notifications occur
- Using a real metrics service in tests instead of mocks, making tests slow or flaky
Variations
- Use a callback function instead of a full observer class for simpler cases
- Implement the observer as a Protocol or ABC for better type hints and clarity
Real-world use cases
- Tracking user activity metrics when account settings change in a web app
- Publishing state changes to monitoring dashboards for IoT device status updates
- Recording inventory level changes to trigger replenishment analytics in e-commerce
Sponsored
More from System design patterns
- Build a BFF (Backend for Frontend) Mock Aggregator in Python medium
- Builder pattern for mocking complex objects in Python easy
- Circuit Breaker Pattern in Python: Closed, Open, and Half-Open States medium
- Create a Data Helper Class in Python easy
- Domain Driven Design Aggregate Root Example in Python medium
- Facade Pattern in Python with Mock Simplification medium
Keep learning
Related tutorials and quizzes for this topic.