问题
I got the following function to test:
my_package.db_engine.db_functions.py:
from ..utils import execute_cmd
from my_package.db_engine.db_functions import dbinfo
def dbinfo(db_name):
params = (cmd_cfg.DB, add_pj_suffix(db_name))
cmd = get_db_cmd_string(cmd_cfg.DBINFO, params=params)
cmd_result = execute_cmd(cmd)
result_dict = map_cmd_output_to_dict(cmd_result)
return result_dict
This function takes the name of a database, then builds a command string from it and executes this command as subprocess
with the execute_cmd
method.
I want to test this function without actually executing the subprocess
. I only want to check if the command is built correctly and correctly passed to execute_cmd
. Therefore I need to mock the execute_cmd
method which is imported from module utils
.
My folder structure is the following:
my_project
|_src
| |_my_package
| | |_db_engine
| | | |_db_functions.py
| | | |_ __init__.py
| | |_utils.py
| | |_ __init__.py
| | |_ ....
| |_ __init__.py
|_tests
|_test_db_engine.py
So for my test I tried the following in test_db_engine.py
:
import unittest
from mock import patch
from my_pacakge.db_engine.db_functions import dbinfo
def execute_db_info_cmd_mock():
return {
'Last Checkpoint': '1.7',
'Last Checkpoint Date': 'May 20, 2015 10:07:41 AM'
}
class DBEngineTestSuite(unittest.TestCase):
""" Tests für DB Engine"""
@patch('my_package.utils.execute_cmd')
def test_dbinfo(self, test_patch):
test_patch.return_value = execute_db_info_cmd_mock()
db_info = dbinfo('MyDBNameHere')
self.assertEqual(sandbox_info['Last Checkpoint'], '1.7')
The execution of the actual command yields 1.6
for Last Checkpoint
. So to verify if the mock return value is used, I set it to 1.7
.
But the mock for the function is not used, as the execution of the test case still yields 1.6
because it is executing the actual function that should have been patched with the mock.
Any idea what I got wrong here?
回答1:
You are patching the wrong location. From the Where to patch section:
patch()
works by (temporarily) changing the object that a name points to with another one. There can be many names pointing to any individual object, so for patching to work you must ensure that you patch the name used by the system under test.The basic principle is that you patch where an object is looked up, which is not necessarily the same place as where it is defined.
Your code-under-test finds execute_cmd
as a global in their own module, but you didn't patch that reference:
from ..utils import execute_cmd
The my_package.utils.execute_cmd
reference is patched, but that execute_cmd
reference in my_package.db_engine.db_functions
will still point to the original, unpatched function.
Patch the imported global instead:
@patch('my_package.db_engine.db_functions.execute_cmd')
Now the execute_cmd
lookup inside dbinfo
will use the patched mock object rather than the original global bound by the from ... import ...
statement.
来源:https://stackoverflow.com/questions/46707483/how-to-mock-a-function-that-is-imported-within-an-imported-method-from-differen