python mocked function not called
Asked Answered
A

3

6

I'm testing python code (A django 3.0.5 project although I don't think it's relevant), but I can't get the functions of my mocked objects to be called. Here is my code:

**myproject.mypackage.myhelpers**


def get_dict():
    return dict()

**myproject.mypackage.mythings**

from .myhelpers import get_dict


def use_dict():
    the_dict = get_dict()
    pass
    return


**myproject.tests.test_mythings**

from ..mypackage import mythings
import unittest
import unittest.mock as mock


class MyThingsTests(unittest.TestCase):

    @mock.patch('myproject.mypackage.myhelpers')
    def test_using_dict(self, mock_myhelpers):
        test_dict = {
            "hi": "foo",
            "there": "bar",
            "sir": "foobar"
        }

        mock_myhelpers.get_dict.return_value = test_dict

        mythings.use_dict()

        mock_myhelpers.get_dict.assert_called_once()

However in the end the test fails with error:

AssertionError: Expected 'get_dict' to have been called once. Called 0 times

Aspirant answered 16/7, 2020 at 8:6 Comment(0)
A
17

Try this instead:

@mock.patch('myproject.mypackage.mythings.get_dict')
def test_using_dict(self, mock_get_dict):
    test_dict = {
        "hi": "foo",
        "there": "bar",
        "sir": "foobar"
    }

    mock_get_dict.return_value = test_dict

The Where to patch section of the docs explains it a bit.

From how I understand it, myproject.mypackage.mythings will already have imported the "real" get_dict before you do the patch. So if you patch it like @mock.patch('myproject.mypackage.myhelpers'), only the myhelpers module will "know" that it is patched. The mythings module will still have the reference to the real get_dict.

I think an alternative to the way of patching I did above, is to change how you import get_dict instead. Instead of importing get_dict directly, just import myhelpers and use get_dict as myhelpers.get_dict. Then you should be able to keep your way of patching the same because get_dict will be looked up from the myhelpers which would have the patched method.

Antidisestablishmentarianism answered 16/7, 2020 at 8:28 Comment(3)
You're exactly right! Or, at least what you suggested made the test passAspirant
This is the answer. I'd like to add that when thinking about patching, one must think about imports during execution time, rather than how they're written. When you're patching method_a to test module_b having in module_b an import method_a from module_a, the patch you want is @patch('module_b.method_a').Peshitta
@thlik, this was not clear to me from the documentation at all. I'd like to try and summarize it. Patching must use the path from where it is used! for that specific context. Not from where it is defined.Barnacle
H
1

Solution 1

  • replace myproject.mypackage.myhelpers with myproject.mypackage.mythings
# @mock.patch('myproject.mypackage.myhelpers')
@mock.patch('myproject.mypackage.mythings.get_dict')
def test_using_dict(self, mock_get_dict):
    test_dict = {
        "hi": "foo",
        "there": "bar",
        "sir": "foobar"
    }

    mock_get_dict.return_value = test_dict

Solution 2

  • replace from .myhelpers import get_dict with import .myhelpers as helpers
**myproject.mypackage.mythings**

# from .myhelpers import get_dict
import .myhelpers as helpers

def use_dict():
    the_dict = helpers.get_dict()
    pass
    return

Summary

  • solution 1 is better since it won't bother you to update the import style in all your .py files
Hagerman answered 20/2, 2022 at 14:22 Comment(0)
L
0

Define the patch at the location where the patch is invoked from

✅
myproject.mypackage.mythings

NOT at the location where the patch is defined at

❌    
myproject.mypackage.myhelpers
Longstanding answered 12/7, 2024 at 9:8 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.