Mock patch the return value of an inner function that's not returned from the outer function

Question:

I’d like to capture the return value of an inner function without having to explicitly return that return value from the outer function.

So I’d like to do something like this:

# bar.py
import foo

def my_fn():
    foo.fn()
# test.py
from mock import patch
import foo
import bar

@patch("foo.fn")
def test_bar(mock_foo_fn):
    bar.my_fn()
    # assert isinstance(mock_foo_fn.return_value, dict)

without having to do this:

# bar.py
import foo

def my_fn():
    return foo.fn()
Asked By: aweeeezy

||

Answers:

This might be more complicated than necessary, but I think you need to patch foo.fn with a callable object that capture the return value of the original function, and not just a Mock. Something like

class Wrapper:
    def __init__(self, f):
        self.f = f

    def __call__(self, *args, **kwargs):
        self.rv = self.f(*args, **kwargs)

with patch('foo.fn', new=Wrapper(foo.fn)) as m:
    my_fn()
    assert isinstance(m.rv, dict)

(Using something like with patch('foo.fn', wraps=foo.fn) is not sufficient, because while that ensures that foo.fn itself is called when the mock is called, the mock itself doesn’t not capture the resulting return value.)

Answered By: chepner

I have a cleaner solution, using a spy.

The mocker.spy object acts exactly like the original method in all cases, except the spy also tracks function/method calls, return values and exceptions raised.

The spy is provided from the mocker fixture of the plugin pytest-mock. Usage demo for your example:

# test.py
import pytest

import bar

def test_bar(mocker):
    spy = mocker.spy(bar.foo, "fn")
    bar.my_fn()
    spy.assert_called_once_with()
    assert spy.spy_return == {"k": "v"}
Answered By: wim
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.