Daniel Quinn
Daniel Quinn

Reputation: 6418

How to mock a function imported from inside another function or method?

I don't make a habit of this, but sometimes, in an effort to work around a circular import, I'll import a function from inside another function or method like this:

class MyClass:
    def my_method(self):
        from somewhere import the_thing
        x = the_thing()
        return x + 4

This works just fine, but I can't figure out how to test it. My usual fails with complaints that module "xyz" has no attribute "the_thing":

from unittest import TestCase
from unittest.mock import patch


class MyTestCase(TestCase):
    @patch("path.to.my.module.the_thing")
    def test_stuff(self):
        ...

Is there a way to mock functions imported at runtime? Is there a Better Way to do this?

Upvotes: 1

Views: 2320

Answers (1)

User051209
User051209

Reputation: 2548

I have created following files in my system.

|-module
|   |- __init__.py
|   |- my_class.py
|   |- xyz.py
|- test_my_class.py

module is a folder with inside the file my_class.py and the module xyz.py besides that the empty file __init__.py.
The production code inside the package module is the following.

File xyz.py:

def the_thing():
    return 6

File my_class.py:

class MyClass:
    def my_method(self):
        from module.xyz import the_thing
        x = the_thing()
        return x + 4

The content of the file test_my_class.py is:

import unittest
from unittest.mock import patch
from module.my_class import MyClass

class MyTestCase(unittest.TestCase):

    @patch("module.xyz.the_thing")
    def test_stuff(self, mock_thing):
        mock_thing.return_value = 20
        self.assertEqual(24, MyClass().my_method())

    def test_stuff_2(self):
        self.assertEqual(10, MyClass().my_method())


if __name__ == '__main__':
    unittest.main()

As you can see the test file defines 2 tests and only in the first test (test_stuff()) is used the patch() function:

  • by patch() is defined the Mock object mock_thing, which is passed to the test method as argument;
  • in the method test_stuff() is defined the return_value of the Mock object mock_thing which substitute the production function the_thing()

The second test method (test_stuff_2()) calls the simple production code of my_method() and verifies its normal return value.

Upvotes: 3

Related Questions