Python Testing Tip #11 / Oct. 14, 2025

How to mock different return values for each call of a method

When the same method is called multiple times in your code, you often need each call to behave differently — for example, fail first and then succeed on retry. Instead of writing multiple mocks or custom logic, you can use MagicMock.side_effect to define what happens on each consecutive call.

# test_weather.py
# test_weather.py
from unittest.mock import MagicMock
from weather import get_temperature_with_retry

def test_retries_after_transient_failure():
    client = MagicMock()

    # First two calls raise TimeoutError, third call succeeds
    client.get_current.side_effect = [
        TimeoutError(),
        TimeoutError(),
        {"temp_c": 21},
    ]

    temp = get_temperature_with_retry(client, "Ljubljana", retries=3)
    assert temp == 21
    assert client.get_current.call_count == 3
# weather.py
# weather.py
from typing import Protocol

class WeatherClient(Protocol):
    def get_current(self, city: str) -> dict:
        """Return {'temp_c': int} or raise TimeoutError on failure."""

def get_temperature_with_retry(client: WeatherClient, city: str, retries: int = 3) -> int:
    attempts = 0
    while attempts < retries:
        try:
            data = client.get_current(city)
            return data["temp_c"]
        except TimeoutError:
            attempts += 1
    raise RuntimeError("weather service unavailable after retries")

Share this tip

The complete testing system, not just tips.

Stop piecing together advice from blog posts. This course gives you a structured approach to Python testing that scales with your codebase and keeps your AI agents in check.

Get the Course $20