In unittest.mock, what does a Mock's return_value attribute control?
answer
- What a mock gives back when called
- One object, arguments ignored entirely
- Attribute chains configure child mocks
- Same object each call, by identity
- Unset still returns a stable child
basics
~10 sreturn_value is the object a Mock hands back on every call, whatever arguments it receives. Setting client.evaluate.return_value to a dict makes every client.evaluate(...) call return that same dict object, not a fresh copy.
solid answer
~40 s`return_value` is the single object a `unittest.mock.Mock` returns each time it is called, and it ignores the call arguments entirely — a mock is not a dispatch table. Because attribute access on a bare `Mock` auto-creates a child mock, `client.evaluate.return_value = {"enabled": True}` configures the *child* `client.evaluate`, not `client` itself, and the same chaining works to any depth: `client.session.get.return_value.status = 200`. The value comes back by identity, so if the code under test mutates it, later calls see the mutation. If you never set it, calling the mock is still deterministic: it returns an auto-created child mock, the same object every time, reachable as `mock.return_value`. When one fixed object is not enough — varying results, or an exception — you need `side_effect` instead.
code
python · 11 linesfrom unittest.mock import Mock
client = Mock()
client.evaluate.return_value = {"enabled": True}
first = client.evaluate("dark-mode")
second = client.evaluate("anything-else")
print(first, first is second)
client.session.get.return_value.status = 200
print(client.session.get("/flags").status)go deeper
Be ready to say that return_value is what the mock returns when called, and to set it on the exact attribute your code calls — client.evaluate.return_value, not client.return_value.
Explain that attribute access auto-creates and memoises child mocks, so chained configuration works to any depth, and that the same object comes back by identity on every call regardless of arguments.
Show judgment about when a fixed return_value is too blunt: shared mutable results, per-call variation and error paths all need side_effect, and an unconfigured mock returning a truthy child can make a test pass for the wrong reason.
Own the guidance on how far a team should script return values at all. Deep return_value chains bake a collaborator's internal shape into every test; a thin hand-written fake or a narrower seam usually costs less to maintain.
A `unittest.mock.Mock` is a callable object that accepts any call, records it, and returns something. `return_value` is the attribute that decides what that something is, and it is the first thing to learn about configuring a test double in Python. ### One object, regardless of arguments Setting `return_value` installs exactly one object as the result of every call. A mock is not a lookup table: it does not match on arguments, so `m(1)` and `m("anything", key=None)` return the identical object. Candidates often expect argument-sensitive behaviour and are surprised when a mock configured "for one input" answers every input the same way. If the result genuinely must depend on the arguments, `return_value` is the wrong tool and a callable `side_effect` is the right one. ### Auto-created children, and where to set it Any attribute you touch on a bare `Mock` is created on demand as a child mock and memoised, so the same attribute access always yields the same child. That is what makes the idiomatic one-liner work: ```python client = Mock() client.evaluate.return_value = {"enabled": True} ``` Here `client.evaluate` materialises a child mock and `return_value` is set on that child — not on `client`. A common beginner error is `client.return_value = {...}`, which configures what `client()` returns and leaves `client.evaluate()` returning a mock. Because children are memoised, chains compose to arbitrary depth: `client.session.get.return_value.status = 200` walks `client.session`, then `.get`, then the mock that `get()` returns, and sets `status` on it. That depth is powerful and dangerous: every dot in such a chain is an assumption your test makes about the collaborator's internal shape. ### The value is returned by identity `return_value` is stored, not copied. The consequence bites in real suites: if the code under test pops a key from the returned dict, appends to the returned list, or closes the returned handle, the *next* call receives the already-mutated object. When each call must get clean state, build a fresh object per call with a callable `side_effect`, or supply a list of separate objects. Even unconfigured, `return_value` exists. The first time the mock is called (or the first time you read `mock.return_value`) a child mock is created and reused thereafter, so `m() is m()` is `True` and `m() is m.return_value` is `True`. This is convenient, and it is also the classic source of a test that passes for the wrong reason: a mock result is truthy, is not `None`, supports attribute access and, if it is a `MagicMock`, supports many dunder operations too. An assertion like `assert result` can therefore pass against a collaborator you forgot to configure. ### Relationship with side_effect `side_effect` is consulted first. If it is set to an exception, an iterable or a callable, `return_value` is bypassed — with one deliberate exception: a callable `side_effect` that returns the `unittest.mock.DEFAULT` sentinel falls back to `return_value`. That pairing lets one callable special-case a few interesting arguments and delegate everything else to the configured default. With `side_effect` set back to `None`, plain `return_value` behaviour resumes. ### Resetting between tests `mock.reset_mock()` deliberately clears recorded usage but leaves configuration in place, precisely because configuration is usually the expensive part of a fixture. To drop the scripted results too, pass the explicit flags: `mock.reset_mock(return_value=True, side_effect=True)`. In practice a fresh mock per test is safer than a shared, mutated one, and re-creating the double per test avoids an entire class of order-dependent failures. ### When it is the right tool `return_value` is the right tool when the collaborator's answer is uninteresting to the test — you need *a* value so the code under test can proceed, and the real assertion is about what your code does with it. It is the wrong tool when the test is about a sequence of outcomes, an error path, or argument-dependent behaviour. Reaching for a three-level `return_value` chain is a signal worth heeding: at that depth the test is describing someone else's internals, and a small hand-written fake, or a narrower seam in the code, usually ages better. These semantics are unchanged across Python 3.10–3.14; `unittest.mock` has been part of the standard library since 3.3. ### The most-missed case: a mocked callable that stands in for a constructor When the code under test builds its collaborator itself, the double you install stands in for the *class*, and calling it returns that mock's `return_value`. So the object your code actually works with is `mock_cls.return_value`, and that is where the method configuration belongs: `mock_cls.return_value.evaluate.return_value = {"enabled": True}`. Candidates who set `mock_cls.evaluate.return_value` instead are configuring a method on the class double that nobody calls, and their test silently exercises an unconfigured instance. The same reasoning explains why every constructed instance in the test is the *same* object: one `return_value`, handed back on each construction.
- If you never set return_value, what does calling a bare unittest.mock.Mock give you?An auto-created child mock — the same object on every call, also reachable as `mock.return_value`. That is why chains like `mock().foo().bar` work with no setup: each call and attribute access materialises a mock and memoises it. It also means the result is truthy and not `None`, so an unconfigured collaborator can make an assertion pass for the wrong reason.
- The code under test mutates the dict a mock returns. Why does the next call see the mutation?`return_value` holds one object and returns it by identity, never a copy. If the code pops a key from that dict, the second call receives the mutated dict. When each call must get fresh state, use a callable `side_effect` that builds a new object per call, or an iterable of distinct objects.
- How do you clear a configured return_value between tests?`mock.reset_mock()` clears recorded usage but deliberately leaves configuration alone; pass `mock.reset_mock(return_value=True, side_effect=True)` to drop the scripted results too. In practice, creating a fresh mock per test is safer than resetting a shared one, because it removes any chance of order-dependent leakage between tests.
A vending machine wired to one slot: whichever buttons you press, the same item drops out — and it is literally the same item, handed back again, not a fresh one.
saying these in an interview costs you the question
- Thinks return_value varies with the call arguments
- Sets return_value on the parent instead of the called child
- Assumes each call gets a fresh copy of the object
- Believes an unconfigured mock call returns None
- Uses return_value expecting a different value per call