I wish to mock a class with the following requirements:
__init__()
methodautospec=True
, so the class's API will be strictly checks on callsA simplified class sample:
class MyClass():
id = 0
def __init__(self, x=0.0, y=1.0):
self.x = x
self.y = y
self.id = MyClass._id
MyClass.id +=1
def calc_x_times_y(self):
return self.x*self.y
def calc_x_div_y(self, raise_if_y_not_zero=True):
try:
return self.x/self.y
except ZeroDivisionError:
if raise_if_y_not_zero:
raise ZeroDivisionError
else:
return float('nan')
I need for the mock object to behave as the the original object, as far as properties are concerned:
x,y
properties
But the mock method calls should be intercepted by the mock, and have its call signature validatedWhat's the best way to go on about this?
EDIT
I've already tried several approaches, including subclassing the Mock
class, use attach_mock()
, and mock_add_spec()
, but always ran into some dead end.
I'm using the standard mock library.
Auto-speccing can be done through the autospec argument to patch, or the create_autospec() function. Auto-speccing creates mock objects that have the same attributes and methods as the objects they are replacing, and any functions and methods (including constructors) have the same call signature as the real object.
To mock objects, you use the unittest. mock module. The unittest. mock module provides the Mock class that allows you to mock other objects.
Mocking is simply the act of replacing the part of the application you are testing with a dummy version of that part called a mock. Instead of calling the actual implementation, you would call the mock, and then make assertions about what you expect to happen.
Since no answers are coming in, I'll post what worked for me (not necessarily the best approach, but here goes):
I've created a mock factory which creates a Mock()
object, sets its id
property using the syntax described here, and returns the object:
class MyClassMockFactory():
_id = 0
def get_mock_object(self, *args,**kwargs):
mock = Mock(MyClass, autospec = True)
self._attach_mock_property(mock , 'x', kwargs['x'])
self._attach_mock_property(mock , 'y', kwargs['y'])
self._attach_mock_property(mock , 'id', MyClassMockFactory._id)
MyClassMockFactory._id += 1
return mock
def _attach_mock_property(self, mock_object, name, value):
p = PropertyMock(return_value=value)
setattr(type(mock_object), name, p)
Now, I can patch the MyClass()
constructor for my tests:
class TestMyClass(TestCase):
mock_factory = MyClassMockFactory()
@patch('MyClass',side_effect=mock_factory.get_mock_object)
test_my_class(self,*args):
obj0 = MyClass()
obj1 = MyClass(1.0,2.2)
obj0.calc_x_times_y()
# Assertions
obj0.calc_x_times_y.assert_called_once_with()
self.assertEqaul(obj0.id, 0)
self.assertEqaul(obj1.id, 1)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With