Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to mock a function, that is imported within an imported method from different module

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?

like image 796
Igle Avatar asked Oct 12 '17 10:10

Igle


People also ask

How do you mock a function from another module?

There are two ways to mock functions: Either by creating a mock function to use in test code, or writing a manual mock to override a module dependency.

How do you mock an import jest?

To mock an imported function with Jest we use the jest. mock() function. jest. mock() is called with one required argument - the import path of the module we're mocking.

What is Side_effect in mock Python?

side_effect: A function to be called whenever the Mock is called. See the side_effect attribute. Useful for raising exceptions or dynamically changing return values. The function is called with the same arguments as the mock, and unless it returns DEFAULT , the return value of this function is used as the return value.


1 Answers

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.

like image 143
Martijn Pieters Avatar answered Sep 20 '22 09:09

Martijn Pieters