Trouble with mocking

Mark Bourne nntp.mbourne at spamgourmet.com
Fri Sep 20 15:00:57 EDT 2024


Norman Robins wrote:
> I'm somewhat new to mocking for unit tests.
> 
> I have some code like this:
> 
> In foo/bar/baz.py I have 2 function I want to mock, one calls the other"
> def function1_to_mock():
>     .
>     .
>     .
> 
> def function2_to_mock():
>     function1_to_mock()
> 
> In foo/bar/main.py I import 1 of these and call it"
> from .baz import function2_to_mock
> 
> def some_function():
>      function1_to_mock()

I'm assuming this is supposed to be calling `function2_to_mock`? 
(Otherwise the import should be for `function1_to_mock`, but then the 
fact `function2_to_mock` also calls `function1_to_mock` would be irrelevant)

>      .
>      .
>      .
> 
> I want to mock both function1_to_mock and function2_to_mock
> 
> In my test I do this:
> 
> def function1_to_mock(kid):
>      return MOCKED_VALUE
> 
> @pytest.fixture(autouse=True)
> def mock_dependencies():
>      with patch(foo.bar.baz.function1_to_mock') as mock_function1_to_mock, \
>           patch('foo.bar.main.function2_to_mock') as mock_function2_to_mock:
>          mock_function2_to_mock.return_value = {
>              'this': 'that
>          }
>          yield mock_function1_to_mock, mock_function2_to_mock
> 
> def test_main(mock_dependencies):
>      some_function()
> 
> When some_function is called the real function1_to_mock is called instead
> of my mock.
> 
> Can someone please let me know how to properly mock these 2 functions.
> 
> Thanks!

In `foo/bar/main.py`, the line:
```
from .baz import function2_to_mock
```
creates a reference named `function2_to_mock` in `main.py` referring to 
the method in `foo/bar/baz.py`.

When you patch `foo.bar.baz.function2_to_mock`, you're patching the 
reference in `foo.bar.baz`, but the reference in `foo.bar.main` still 
refers to the original function object.

There are at least a couple of ways around this.  The way I prefer is to 
change `main.py` to import the `baz` module rather than just the function:
```
 > from . import baz
 >
 > def some_function():
 >      baz.function2_to_mock()
```
Here, `main.py` has a reference to the `baz` module rather than the 
individual function object.  It looks up `function2_to_mock` in `baz` 
just before calling it so, when the `baz` module is patched so that 
`baz.function2_to_mock` refers to a mock, the call in main.py` gets the 
mock and calls that rather than the original function.

There no memory saving by importing just the functions you need from 
`baz` - the whole module is still loaded on import and remains in 
memory, it's just that `main` only gets a reference to the one function. 
  The effect is similar to doing something like:
```
from . import baz
function2_to_mock = baz.function2_to_mock
del baz
```
...including the fact that, after the import, the reference to 
`function2_to_mock` in `main` is just a copy of the reference in `baz`, 
hence not getting updated by the patch.

The other way around it is to patch `main.function2_to_mock` instead of 
patching `foo.bar.baz.function2_to_mock`.

See also the documentation under "where to patch" at 
<https://docs.python.org/3/library/unittest.mock.html#where-to-patch>.

Note that, since you're patching `function2_to_mock`, you don't 
necessarily need to patch `function1_to_mock` as well.  The mock of 
`function2_to_mock` won't call `function1_to_mock` (or its mock) 
regardless of whether `function1_to_mock` has been patched, unless you 
set the mock of `function2_to_mock` to do so.  You don't necessarily 
need to patch `function1_to_mock`, unless of course there are other 
calls to it that you need to mock.

-- 
Mark.


More information about the Python-list mailing list