How to Mock Sequential Calls in Python with unittest.mock

Use Mock.side_effect to return a different result for each sequential call and verify the call order with assert_has_calls.

Medium Python 3.9+ Aug 9, 2026 A/B testing & experimentation 14 views 0 copies

Python code

27 lines
Python 3.9+
import unittest
from unittest.mock import Mock

class Service:
    def fetch(self, item_id):
        raise NotImplementedError

def process_items(service, ids):
    results = []
    for item_id in ids:
        result = service.fetch(item_id)
        results.append(result)
    return results

if __name__ == "__main__":
    mock_service = Mock(spec=Service)
    mock_service.fetch.side_effect = [10, 20, 30]
    
    output = process_items(mock_service, [1, 2, 3])
    print(output)
    
    mock_service.fetch.assert_has_calls([
        unittest.mock.call(1),
        unittest.mock.call(2),
        unittest.mock.call(3)
    ])
    print("Calls verified")

Output

stdout
[10, 20, 30]
Calls verified

How it works

The side_effect list makes the mock return 10 on the first call, 20 on the second, and 30 on the third — perfect for simulating a service with stateful responses. Each time mock_service.fetch is called inside the loop, it pops the next value from the list. assert_has_calls checks that the mock was called with arguments 1, 2, and 3 in that exact order, matching the iteration over ids. This pattern lets you test code paths that depend on sequential, ordered results without needing a real backend.

Common mistakes

  • Using `return_value` instead of `side_effect` when each call needs a different result.
  • Forgetting `spec=Service` to enforce only real service methods are callable.
  • Calling `assert_has_calls` without also asserting the call count when order matters.
  • Assuming `side_effect` with a list works for keyword arguments — it only handles positional calls.

Variations

  1. Use a generator function for `side_effect` to compute returns lazily based on input.
  2. Use `mock_service.fetch.side_effect = [10, 20, 30]` with `unittest.mock.patch` to avoid creating a Mock manually.

Real-world use cases

  • Simulating response variations in an A/B test where treatment groups get different computed values.
  • Mocking a metrics service that returns distinct counts on each polling cycle in a dashboard.
  • Stubbing an LLM's temperature-controlled outputs so each sequential prompt gets a stable different reply.

Sponsored

Run this sample

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

Open editor

More from A/B testing & experimentation

Related tutorials and quizzes for this topic.