The Production file (production_file.py) is:
class MyError(Exception):
pass
class MyClass:
def __init__(self):
self.value = None
def set_value(self, value):
self.value = value
def foo(self):
raise RuntimeError("error!")
class Caller:
def bar(self, smth):
obj = MyClass()
obj.set_value(smth)
try:
obj.foo()
except MyError:
pass
obj.set_value("str2")
obj.foo()
Test file (test.py):
import unittest
from unittest.mock import patch
from unittest.mock import call
from production_file import MyClass, Caller
class MyTest(unittest.TestCase):
def test_caller(self):
with patch('production_file.MyClass', autospec=MyClass) as MyClassMock:
my_class_mock_obj = MyClassMock.return_value
my_class_mock_obj.foo.side_effect = [MyError("msg"), "text"]
caller = Caller()
caller.bar("str1")
calls = [call("str1"), call("str2")]
my_class_mock_obj.set_value.assert_has_calls(calls)
if __name__ == '__main__':
unittest.main()
This above works. But if I move the production classes (MyError, MyClass, Caller) into the test file, and update patch to:
with patch('test.MyClass', autospec=MyClass) as MyClassMock:
then the instance method "foo" is no longer mocked.
Does anybody have any idea why that is?
I have also experienced a similar problem with some more complex code, where the production code is in my_package/src/production_file.py while the test is in my_package/tests/test_file.py. Python yields no error for the path, the path is correct, but still the mock doesn't work.
If you are running
test.py
as__main__
then it is nottest.MyClass
it would be__main__.MyClass
, or in both cases__name__+".MyClass"
.I was able to determine that the class used and the class patched were different by adding a print statement:
When the patch is applied to the class that this is using you would see something like this:
But when the class in moved into
test.py
you will see something like:Which indicates:
MyClass
(at least the one that is used for the test.)__main__.MyClass
However It is quite likely that your "more... complicated situation" is not working because of a setup like this:
In this case
production_file.MyClass
is being patched andMyClass
is being imported fromproduction_file
so the correct class is being patched but still the output is:This is because the Class was directly imported to the local namespace, so when the patch is applied to the
production_file
the local namespace is still unaffected, we can check that the patch was actually applied with:If this is the case you just need to import the module, not the class directly. Then once the patch is applied you will be using it right from the file: