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.
Python code
27 linesimport 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
[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
- Use a generator function for `side_effect` to compute returns lazily based on input.
- 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
More from A/B testing & experimentation
Keep learning
Related tutorials and quizzes for this topic.