• Re: Trouble with mocking

    From Mark Bourne@21:1/5 to Norman Robins on Fri Sep 20 20:00:57 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.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)