python3 mock doesn't work for all paths - python-3.x

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 not test.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:
class Caller:
def bar(self, smth):
print(MyClass) #lets see what we are actually making an instance of...
obj = MyClass()
...
When the patch is applied to the class that this is using you would see something like this:
<MagicMock name='MyClass' spec='MyClass' id='4387629656'>
But when the class in moved into test.py you will see something like:
<class '__main__.MyClass'>
Which indicates:
There was no patching applied to MyClass (at least the one that is used for the test.)
The name of the class that needs to be patched is __main__.MyClass
However It is quite likely that your "more... complicated situation" is not working because of a setup like this:
from production_file import MyClass
class MyError(Exception):
pass
class Caller:
def bar(self, smth):
print(MyClass)
obj = MyClass()
...
class MyTest(unittest.TestCase):
def test_caller(self):
with patch('production_file.MyClass', autospec=MyClass) as MyClassMock:
...
In this case production_file.MyClass is being patched and MyClass is being imported from production_file so the correct class is being patched but still the output is:
<class 'production_file.MyClass'>
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:
...
def bar(self, smth):
print(MyClass)
from production_file import MyClass as pf_MyClass
print(pf_MyClass)
...
#output:
<class 'production_file.MyClass'>
<MagicMock name='MyClass' spec='MyClass' id='4387847136'>
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:
import production_file
...
class Caller:
def bar(self, smth):
print(production_file.MyClass)
obj = production_file.MyClass()
...
class MyTest(unittest.TestCase):
def test_caller(self):
with patch('production_file.MyClass', autospec=MyClass) as MyClassMock:
...

Related

Pytest object created by object assert_called_once_with

I known how I can test if an injected object was called with a specific argument. But in my case the injected object will create an object that object will create another object and I want to test if that last object was called with the right argument.
in the example below the question would be if c.dirve was called with 100 as argument:
class car:
def drive(self, distance):
print("so fast")
class car_shop:
def buy_car(self):
return car()
class shop_shop:
def buy_shop(self):
return car_shop()
class processor:
def __init__(self, sshop):
self.sshop = sshop
def run(self):
cshop = self.sshop.buy_shop()
c = cshop.buy_car()
c.drive(100)
def main():
sshop = shop_shop()
proc = processor(sshop)
proc.run()
if __name__ == "__main__":
main()
is there a way to test that?
Since this was requested here my approach for testing these objects:
import pytest
from unittest.mock import Mock
from object_returns_object_test_for_arguments import processor, shop_shop
#pytest.fixture
def mock_shop_shop():
return Mock(spec=shop_shop)
def test_processor_car_called_with_100(mock_shop_shop):
proc = processor(mock_shop_shop)
proc.run()
assert mock_shop_shop.car_shop.car.drive.assert_called_once_with(100)
assert mock_shop_shop.car_shop.car.drive.call_count == 1
If using just the code shown in the question, you only have to mock car.drive. This could be done for example this way:
from unittest import mock
from object_returns_object_test_for_arguments import processor, shop_shop
#mock.patch('object_returns_object_test_for_arguments.car.drive')
def test_processor_car_called_with_100(drive_mock):
proc = processor(shop_shop())
proc.run()
drive_mock.assert_called_once_with(100)
As I don't know your real code, you may have to mock more stuff.
As an aside: class names in Python are written upper-case, camelcase-style by default.

How to use a pytest fixture to instantiate a object under test?

It appears that fixtures should be used to instantiate an object under test for pytest, especially if it is used by several test_ functions. However, after trying to adapt examples given in the pytest doc, I cannot get the following to work.
import pytest
...
#pytest.fixture
def newMyClass():
obj = MyClass(1,2)
...
def test_aMethod(newMyClass):
objectUnderTest = newMyClass.obj
...
There are no complaints about the fixture or the constructor, but then I receive the pytest error
def test_aMethod(newMyClass):
> objectUnderTest = newMyClass.obj()
E AttributeError: 'NoneType' object has no attribute 'obj'
If fixtures can be used for this, how should that be coded?
To clean up #hoefling's answer, you need to instantiate your class directly and return that instance. Check out this code if you're looking for a cleaned up version.
import pytest
class MyClass():
def __init__(self, obj, foo):
self.obj = obj
self.foo = foo
#pytest.fixture
def newMyClass():
myClassInstance = MyClass(1,2)
return myClassInstance
def test_aMethod(newMyClass):
objectUnderTest = newMyClass.obj
assert objectUnderTest

Python: Defining static class variables by using other static variable

How can I define static class members by using other static members?
for instance:
class Somefuncs:
#staticmethod
def foo():
print('foo was called')
functs_dict={'foo':Somefuncs.foo}
makes the interpreter raise the Exception: Unresolved reference 'Somefuncs'
even though I'm defining funct_dict within the class Somefuncs!
At this point the class is not really defined.
But you could just write:
class Somefuncs:
#staticmethod
def foo():
print('foo was called')
functs_dict={'foo': foo}
# Test it
Somefuncs.functs_dict["foo"]()
# Output: foo was called
It always refers to the current class

Python unittest: Mock function in TestCase class

My approach to mock testing functions looks like this:
from unittest import mock, TestCase
from main import my_function
def my_mock(s):
if s == 'hello':
return 'goodbye'
return my_function(s)
class TestMyFunction(TestCase):
#mock.patch('my_function', side_effect=MyMock.my_mock)
def test_my_function(self, mock_get):
s = 'hello'
self.assertEqual('goodbye', my_function(s))
This works. But if I have multiple tests, where my_mock_1 patches test_my_function_1 and my_mock_2 patches test_my_function_2 and so on, the mock definitions are very far from the test definitions and the code becomes hard to read.
Is there a way to get the mock definition closer to the tests they belong to?
What I tried was
class TestMyFunction(TestCase):
#staticmethod
def my_mock_1(s):
...
#mock.patch('my_function', side_effect=my_mock_1)
def test_my_function_1(self, mock_get):
...
#staticmethod
def my_mock_2(s):
...
#mock.patch('my_function', side_effect=my_mock_2)
def test_my_function_2(self, mock_get):
...
...
But this fails with the exception
TypeError: 'staticmethod' object is not an iterator.

Python magic method for missing class method?

I need for an abstract class to be able to handle missing class method. I find out how to do this for instance method with __getattr__ but it's not working with class method. Is it even possible ?
I've got something like this :
class Container:
_definitions = {'class_a': 'example.class_a.ClassA',
'class_b': 'example.class_b.ClassB'}
#classmethod
def get(cls, def_id):
# import dynamicaly
parts = cls._definitions[def_id].split(".")
module_name = ".".join(parts[:-1])
class_name = parts[-1]
__import__(module_name)
return class_name
class AbstractClass:
#classmethod
def __getattr__(cls, name, *args):
def missing_method():
result = re.search("^(?P<class_id>[a-z0-9._-]*)$", name)
if result:
return Container.get(result.group('class_id'))
raise RuntimeError("class method '{}' missing from class".format(name))
return missing_method
class ClassA(AbstractClass):
#classmethod
def method(cls):
classb = cls.class_a()
classb.method()
class ClassB(AbstractClass):
#classmethod
def method(cls):
print('Hello world')
Each class in my Container must extend AbstractClass to be able to magically call any class in it, like I do un classA.method().
It's working if I do not use class method, but my purpose is that every class into my container can not be instantiate cause it will be useless for my needs. It's a kind of Singleton pattern.
Is it more understandable ?

Resources