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

Time:12-15

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()

CodePudding user response:

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"}

CodePudding user response:

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.)

  • Related