python
mocking
unit testing
multiple return values
unittest.mock

Python mock multiple return values

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

Introduction

A mock often needs to behave differently on successive calls. That happens when you are testing retries, polling loops, pagination, or any code path where the dependency changes state over time. In Python, the usual tool for this is side_effect from unittest.mock.

Use side_effect for Sequential Results

If every call should return the same value, return_value is enough. If the mock should return different values each time, assign an iterable to side_effect.

python
1from unittest.mock import Mock
2
3client = Mock()
4client.fetch_user.side_effect = [
5    {"status": "loading"},
6    {"status": "ready", "name": "Mina"},
7]
8
9print(client.fetch_user())
10print(client.fetch_user())

The first call returns the first item, the second call returns the second item, and so on. This keeps the test focused on behavior instead of on setting up a real external system.

Test Retry Logic Cleanly

One of the most useful cases is retry handling. The dependency can fail once and then succeed on the next call.

python
1from unittest.mock import Mock
2
3
4def fetch_with_retry(api):
5    for _ in range(3):
6        result = api.get()
7        if result["ok"]:
8            return result["data"]
9    raise RuntimeError("request never succeeded")
10
11
12api = Mock()
13api.get.side_effect = [
14    {"ok": False, "data": None},
15    {"ok": True, "data": "final payload"},
16]
17
18print(fetch_with_retry(api))
19print(api.get.call_count)

This is easier to reason about than using sleeps, real sockets, or a temporary test server just to create one failing request.

Mix Return Values and Exceptions

side_effect can also raise exceptions. That is useful for testing recovery code after a temporary failure.

python
1from unittest.mock import Mock
2
3
4def read_after_retry(storage):
5    try:
6        return storage.read()
7    except TimeoutError:
8        return storage.read()
9
10
11storage = Mock()
12storage.read.side_effect = [
13    TimeoutError("temporary timeout"),
14    "cached result",
15]
16
17print(read_after_retry(storage))

When a value inside side_effect is an exception instance or exception class, the mock raises it instead of returning it.

Patch the Symbol Used by the Code Under Test

In real tests, you usually patch a function as imported by the module under test, not just construct a loose mock object. That lookup path matters.

python
1from unittest.mock import patch
2
3import payments
4
5
6def test_charge_retries_then_succeeds():
7    with patch("payments.gateway.charge") as charge:
8        charge.side_effect = ["retry", "approved"]
9        result = payments.charge_order("order-123")
10
11    assert result == "approved"

If the code under test imported the dependency into a different namespace, patch that namespace instead. Many failed mock tests come from patching the original library symbol while the application code is reading a copied reference.

Use a Function When Output Depends on Input

A list is perfect for strictly sequential behavior, but sometimes the return value should vary by arguments. In that case, give side_effect a function.

python
1from unittest.mock import Mock
2
3
4def choose_response(user_id):
5    return {"user_id": user_id, "active": user_id % 2 == 0}
6
7
8service = Mock()
9service.lookup.side_effect = choose_response
10
11print(service.lookup(10))
12print(service.lookup(11))

This keeps the mock compact while still reacting to its inputs like a lightweight fake.

Common Pitfalls

Using return_value when the test really needs changing results hides important control-flow behavior. Use side_effect when successive calls matter.

If the iterable is too short, the mock raises StopIteration. Make sure the number of configured results matches the expected number of calls.

Patching the wrong import path is a classic Python testing mistake. Patch the name as the code under test resolves it.

Summary

  • Use side_effect with a list when successive calls should return different values.
  • Include exceptions in the sequence to test retry and recovery paths.
  • Patch the symbol as used by the code under test, not just the original library symbol.
  • Use a function as side_effect when return values depend on the call arguments.

Related reading
Free course
Beginner
7 lessons
2 hours
Tackling System Design Interview Problems

A short course that equips you with the skills to approach system design interviews methodically.

Start the free course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

All Rights Reserved.