Observer Pattern with Mock Metrics in Python

Implement the Observer pattern with a mock metrics collector to track state changes and verify notifications.

Easy Python 3.9+ Aug 9, 2026 System design patterns 12 views 0 copies

Python code

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

stdout
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

  1. Use a callback function instead of a full observer class for simpler cases
  2. 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

Run this sample

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

Open editor

More from System design patterns

Related tutorials and quizzes for this topic.