如何模拟从不同模块导入的方法中导入的函数

时间:2017-10-12 10:37:28

标签: python unit-testing mocking monkeypatching

我有以下功能要测试:

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

此函数获取数据库的名称,然后从中构建命令字符串,并使用subprocess方法以execute_cmd执行此命令。 我想在不实际执行subprocess的情况下测试此函数。我只想检查命令是否正确构建并正确传递给execute_cmd。因此,我需要模拟从模块execute_cmd导入的utils方法。

我的文件夹结构如下:

my_project
|_src
| |_my_package
| | |_db_engine
| | | |_db_functions.py
| | | |_ __init__.py
| | |_utils.py
| | |_ __init__.py
| | |_ ....
| |_ __init__.py
|_tests
  |_test_db_engine.py

因此,对于我的测试,我在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')

执行实际命令会为1.6生成Last Checkpoint。因此,为了验证是否使用了模拟返回值,我将其设置为1.7。 但是没有使用该函数的模拟,因为测试用例的执行仍然产生1.6,因为它正在执行应该使用模拟修补的实际函数。

知道我错在哪里吗?

1 个答案:

答案 0 :(得分:12)

您正在修补错误的位置。来自Where to patch section

  

patch()通过(暂时)更改名称指向的对象与另一个对象。可以有许多名称指向任何单个对象,因此要修补工作,您必须确保修补被测系统使用的名称。

     

基本原则是您在查找对象的位置进行修补,这不一定与定义对象的位置相同。

您的测试代码在自己的模块中找到execute_cmd作为全局,但您没有修补该引用:

from ..utils import execute_cmd

my_package.utils.execute_cmd引用已修补,但execute_cmd中的my_package.db_engine.db_functions引用仍指向原始未修补的函数。

修补导入的全局:

@patch('my_package.db_engine.db_functions.execute_cmd')

现在execute_cmd内的dbinfo查找将使用修补的模拟对象,而不是from ... import ...语句绑定的原始全局。