At Most Once Fire-and-Forget Mock in Python

A Python mock that enforces send() is called at most once and records the arguments for verification.

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

Python code

27 lines
Python 3.9+
class FireForgetMock:
    def __init__(self):
        self._calls = 0
        self._last_args = None
        self._last_kwargs = None

    def send(self, *args, **kwargs):
        if self._calls > 0:
            raise RuntimeError("send() called more than once")
        self._calls += 1
        self._last_args = args
        self._last_kwargs = kwargs

    def assert_called_once(self):
        if self._calls != 1:
            raise AssertionError(f"Expected exactly 1 call, got {self._calls}")

    def get_call_details(self):
        return self._last_args, self._last_kwargs


if __name__ == "__main__":
    mock = FireForgetMock()
    mock.send("notification-123", priority="high")
    mock.assert_called_once()
    args, kwargs = mock.get_call_details()
    print(f"Args: {args}, Kwargs: {kwargs}")

Output

stdout
Args: ('notification-123',), Kwargs: {'priority': 'high'}

How it works

The mock uses a counter _calls to track invocations. On the first call, it stores the arguments and increments the counter; any subsequent call raises a RuntimeError, enforcing at-most-once semantics. The assert_called_once method checks that exactly one call happened, failing with a descriptive assertion otherwise. The recorded arguments are retrievable via get_call_details, making it easy to verify what was sent in tests.

Common mistakes

  • Forgetting to reset the mock between test cases, causing state leakage
  • Checking `args` or `kwargs` length without handling the case where they might be empty
  • Raising `AssertionError` without including the call count, making failures hard to debug

Variations

  1. Use `unittest.mock.Mock` with `side_effect` to raise on multiple calls
  2. Use a delegate function that appends to a list for more complex call history

Real-world use cases

  • Stubbing a message queue producer to ensure an event is published only once per request.
  • Verifying that a webhook notifier sends exactly one notification in a rate-limited service.
  • Testing an idempotent job that must trigger a downstream API call at most once.

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.