How to properly mock a function that instantiates a class variable? - python

I have something like this in my source file
# code.py
def some_func():
# doing some connections and stuff
return {'someKey': 'someVal'}
class ClassToTest:
var = some_func()
My test file looks like this... I am trying to mock some_func as I want to avoid creating connections.
# test_code.py
from src.code import ClassToTest
def mock_function():
return {"someOtherKey": "someOtherValue"}
class Test_Code(unittest.TestCase):
#mock.patch('src.code.some_func', new=mock_function)
def test_ClassToTest(self):
self.assertEqual(ClassToTest.var, {"someOtherKey": "someOtherValue"})
But this doesn't work. On the other hand if var is an instant variable mock works fine. I guess this is due to class variables getting initialized during imports. How do I properly mock some_func before var even gets initialized?

When you imported code.py, there is no active patch yet, so when ClassToTest.var was initialized, it used the original some_func. Only then would the patch for src.code.some_func would take effect which obviously is too late now.
Solution 1
What you can do is to patch some_func and then reload code.py so that it re-initializes the ClassToTest including its attribute var. Thus since we already have an active patch by the time we reload code.py, then ClassToTest.var would be set with the patched value.
But we can't do it if both the class and the patched function lives in the same file, so to make it testable move some_func to another file and then just import it.
src/code.py
from src.other import some_func
class ClassToTest:
var = some_func()
src/other.py
def some_func():
# doing some connections and stuff
return {'realKey': 'realValue'}
test_code.py
from importlib import reload
import sys
import unittest
from unittest import mock
from src.code import ClassToTest # This will always refer to the unpatched version
def mock_function():
return {"someOtherKey": "someOtherValue"}
class Test_Code(unittest.TestCase):
def test_real_first(self):
self.assertEqual(ClassToTest.var, {"realKey": "realValue"})
#mock.patch('src.other.some_func', new=mock_function)
def test_mock_then_reload(self):
# Option 1:
# import src
# reload(src.code)
# Option 2
reload(sys.modules['src.code'])
from src.code import ClassToTest # This will be the patched version
self.assertEqual(ClassToTest.var, {"someOtherKey": "someOtherValue"})
def test_real_last(self):
self.assertEqual(ClassToTest.var, {"realKey": "realValue"})
Output
$ pytest -q
... [100%]
3 passed in 0.04s
Solution 2
If you don't want the real some_func to be ever called during test, then just reloading isn't enough. What needs to be done is to never import the file containing ClassToTest nor any file that would import it indirectly. Only import it once an active patch for some_func is already established.
from importlib import reload
import sys
import unittest
from unittest import mock
# from src.code import ClassToTest # Remove this import!!!
def mock_function():
return {"someOtherKey": "someOtherValue"}
class Test_Code(unittest.TestCase):
#mock.patch('src.other.some_func', new=mock_function)
def test_mock_then_reload(self):
from src.code import ClassToTest # Move the import here once the patch has taken effect already
self.assertEqual(ClassToTest.var, {"someOtherKey": "someOtherValue"})

Related

monkeypatching not carrying through class import

I'm trying to test some code using pytest and need to change a function from some module. One of my imports also imports that function, but this is failing when I change the method using monkeypatch. Here is what I have:
util.py
def foo():
raise ConnectionError # simulate an error
return 'bar'
something.py
from proj import util
need_this = util.foo()
print(need_this)
test_this.py
import pytest
#pytest.fixture(autouse=True)
def fix_foo(monkeypatch):
monkeypatch.setattr('proj.something.util.foo', lambda: 'bar')
import proj.something
And this raises ConnectionError. If I change
test_this.py
import pytest
#pytest.fixture(autouse=True)
def fix_foo(monkeypatch):
monkeypatch.setattr('proj.something.util.foo', lambda: 'bar')
def test_test():
import proj.something
Then it imports with the monkeypatch working as expected. I've read though this and tried to model my testing from it, but that isn't working unless I import inside of a test. Why does the monkeypatch not do anything if it is just a normal import in the testing file?
That is because the fixture is applied to the test function not the entire code. autouse=True attribute just says that it should be used in every test

Using patch to to mock a function (as opposed to a method)

I want to do something like the following example (found here)
>>> with patch.object(ProductionClass, 'method', return_value=None) as mock_method:
... thing = ProductionClass()
... thing.method(1, 2, 3)
However this is patching the method called method on ProductionClass. I want to patch a generic function within a context. Ideally something looking like...
with path.something(my_fn, return_value=my_return) as mock_function:
do_some_other_fn()
my_fn is called deep within do_some_other_fn and therefore is difficult to mock out directly. This seems like it should be straight forward but I can't find the right syntax
EDIT In the module that do_some_other_fn lives I import my_fn like followings
from my_module import my_fn
So I need a way to be able to tell mock to patch that from outside the module. Is this possible?
EDIT 2 I think this makes it more clear what I am looking for
This works but is not ideal:
import my_module
with patch('my_module.fn', return_value='hello') as patch_context:
x = my_module.fn()
# x now contains 'hello'
However I would much rather have it work like this (or something similar)
from my_module import fn
with patch('my_module.fn', return_value='hello') as patch_context:
x = fn()
# x contains real result from real call to fn()
Your attempt to patch with from my_module import fn does not work because the import statement creates a local symbol fn which points to whatever value fn has in my_module at the time of import. You later patch my_module.fn but it does not matter because you already have a local copy of fn.
If the file that contains the patch call is the main module (the file that python initially loaded), you should be able to do it by patching __main__.fn:
from my_module import fn
with patch('__main__.fn', return_value='hello') as patch_context:
x = fn()
If the file that contains the patch call is loaded as a module from the main module then __main__ won't work and you need to pass the absolute module name of the module that contains your patch call to patch rather than __main__.
You can see function like module object's static method. To patch a function func in module mymodule you can use
patch("mymodule.func", return_value=my_return)
You should take care of Where to patch and if the function is in the same module where you have the test should use "__main__.func" as patch argument.
patch like patch.object can be useed as decorator, context or by start() and stop() method.
Now when in a module you import a function from an other module like:
from mymodule import func as foo
You create a new reference to func in the new module called foo. Every
time in this module you call foo you will use the reference to mymodule.func that you load when you imported it: if you whould like change this behavior you should patch foo in the new module.
To make it more clear I build an example where you have mymodule that contain func, module_a that include mymodule and use mymodule.func, module_b that use from mymodule import func as foo and use bot foo and mymodule.func
mymodule.py
def func():
return "orig"
module_a.py
import mymodule
def a():
return mymodule.func()
module_b.py
from mymodule import func as foo
import mymodule
def b_foo():
return foo()
def b():
return mymodule.func()
test.py
import unittest
from unittest.mock import *
import mymodule
import module_a
import module_b
class Test(unittest.TestCase):
def test_direct(self):
self.assertEqual(mymodule.func(), "orig")
with patch("mymodule.func", return_value="patched"):
self.assertEqual(mymodule.func(), "patched")
def test_module_a(self):
self.assertEqual(module_a.a(), "orig")
with patch("mymodule.func", return_value="patched"):
self.assertEqual(module_a.a(), "patched")
def test_module_b(self):
self.assertEqual(module_b.b(), "orig")
with patch("mymodule.func", return_value="patched"):
self.assertEqual(module_b.b(), "patched")
self.assertEqual(module_b.b_foo(), "orig")
with patch("module_b.foo", return_value="patched"):
self.assertEqual(module_b.b(), "orig")
self.assertEqual(module_b.b_foo(), "patched")
if __name__ == '__main__':
unittest.main()
In other words what really rules on choosing where to patch is how the function is referenced where you want use the patched version.

Python: Unable to import global variable initialized in setUp method from init file

I have a package with testing modules and inside the init file I have a setUp method with some operations. These operations are executed correctly before any unit test in the package's modules run. Inside the setUp method I'd like to initialize a global variable and then access it from other modules of the package. But this doesn't work.
# TestPackage/__init__.py
def setUp():
global spec_project
core_manager = get_core_manager()
spec_project = core_manager.get_spec()
#TestPackage/test_module.py
from TestPackage import spec_project
import unittest
class TestRules(unittest.TestCase):
def setUp(self):
spec_project.get_new_device()
Like this I get an
ImportError: cannot import name spec_project
If I initialize the spec_project variable outside of the setUp method in the init file I can have access to it but its content is not changed after the operations in the setUp method.
# TestPackage/__init__.py
spec_project = None
def setUp():
global spec_project
core_manager = get_core_manager()
spec_project = core_manager.get_spec()
#TestPackage/test_module.py
from TestPackage import spec_project
import unittest
class TestRules(unittest.TestCase):
def setUp(self):
spec_project.get_new_device()
Like this I get an
AttributeError: 'NoneType' object has no attribute 'get_new_device'
How can initialize the spec_project variable inside the setUp method of the init file and still have access to it from other module in the package?
It looks like setUp() isn't being called, but if you are certain that it is, then it could be the way that you are importing TestPackage. Try importing like this:
#TestPackage/test_module.py
import TestPackage
import unittest
class TestRules(unittest.TestCase):
def setUp(self):
TestPackage.spec_project.get_new_device()
The setUp() method has to be called before you use the global. This same thing should apply to the second way you tried. But again, that is assuming that setUp is run. You can alias TestPackage if you feel it is necessary, or you should be able to import it if it is defined outside the method.
Since you are explicitly importing it, it is likely trying to make a copy of it, which isn't possible, since it is inside of the setUp() body.

Turn off translations for unittest

I have some python code like
from pylons.i18n.translation import _
def get_message():
message = _(u"Translated message")
# interesting code to test
# [...]
return 'result'
which I would like to unittest like this:
class MyTest(TestCase):
def test_get_message(self):
assertTrue(get_message(), 'result')
Now running this test in nosetests gives me:
TypeError: No object (name: translator) has been registered for this thread
Is there a way to deactivate anything regarding translations when unittesting?
Let's say your production code is in my_module.py:
from unittest import TestCase
from mock import patch
from my_module import get_message
class MyTest(TestCase):
def test_get_message(self):
with patch("my_module._"):
result = get_message()
self.assertEqual("result", result)
With the patch your test changes the _() function to a MagicMock() object. Documentation here.
NOTE: mock is part of the standard library from Python 3.3 and onwards. Otherwise you should install it first using pip install mock.

Patch over a function imported inside another function

In order to avoid a circular import, I've been forced to define a function that looks like:
# do_something.py
def do_it():
from .helpers import do_it_helper
# do stuff
Now I'd like to be able to test this function, with do_it_helper patched over. If the import were a top level import,
class Test_do_it(unittest.TestCase):
def test_do_it(self):
with patch('do_something.do_it_helper') as helper_mock:
helper_mock.return_value = 12
# test things
would work fine. However, the code above gives me:
AttributeError: <module 'do_something'> does not have the attribute 'do_it_helper'
On a whim, I also tried changing the patch statement to:
with patch('do_something.do_it.do_it_helper') as helper_mock:
But that produced a similar error. Is there any way to mock this function, given the fact that I'm forced into importing it within the function where it's used?
You should mock out helpers.do_it_helper:
class Test_do_it(unittest.TestCase):
def test_do_it(self):
with patch('helpers.do_it_helper') as helper_mock:
helper_mock.return_value = 12
# test things
Here's an example using mock on os.getcwd():
import unittest
from mock import patch
def get_cwd():
from os import getcwd
return getcwd()
class MyTestCase(unittest.TestCase):
#patch('os.getcwd')
def test_mocked(self, mock_function):
mock_function.return_value = 'test'
self.assertEqual(get_cwd(), 'test')

Categories

Resources