I have an abstract base class:
import abc
import six
@six.add_metaclass(abc.ABCMeta)
class A(object):
@abc.abstractmethod
def f(self, arg1):
pass
I'd like to use this class as a spec for a mock.
import mock
mock_a = mock.Mock(spec=A)
This works partially. For example, mock_a.x
results in AttribureError("Mock object has no attribute 'x')
. However mock_a.f
is not speced based on the abstract method from A.f
. It returns a mock regardless of the number of arguments passed to f
.
mock_a = mock.Mock(spec=A)
# Succeeds
print mock_a.f(1)
# Should fail, but returns a mock
print mock_a.f(1,2)
# Correctly fails
print mock_a.x
Mock can create a mock speced from A.f
with create_autospec
...
f_spec = mock.create_autospec(A.f)
# Succeeds
f_spec(mock_a, 1)
# Correctly fails
f_spec(mock_a, 1, 2)
... but doesn't do so for the attributes of A
How can I create a mock that accurately implements the abstract base class?
Abstract classes make sure that derived classes implement methods and properties defined in the abstract base class. Abstract base class can't be instantiated. We use @abstractmethod to define a method in the abstract base class and combination of @property and @abstractmethod in order to define an abstract property.
A class is called an Abstract class if it contains one or more abstract methods. An abstract method is a method that is declared, but contains no implementation. Abstract classes may not be instantiated, and its abstract methods must be implemented by its subclasses.
There are two options to test an abstract base class (ABC) in Python: you can either override or patch the __abstractmethods__ property to be able to instantiate the abstract class and test it directly or you can create a child class that implements all the abstract methods of the base class.
@abc. abstractmethod prevents any attempt to instantiate a subclass that doesn't override a particular method in the superclass.
Apply mock.create_autospec()
to the class:
>>> mock_a = mock.create_autospec(spec=A)
>>>
>>> # Should succeed
>>> print mock_a.f(1)
<MagicMock name='mock.f()' id='140401932347984'>
>>>
>>> # Should fail
>>> print mock_a.f(1,2)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/lib/python2.7/dist-packages/mock/mock.py", line 1061, in __call__
_mock_self._mock_check_sig(*args, **kwargs)
File "/usr/lib/python2.7/dist-packages/mock/mock.py", line 227, in checksig
sig.bind(*args, **kwargs)
File "/usr/lib/python2.7/dist-packages/mock/mock.py", line 95, in fixedbind
return self._bind(args, kwargs)
File "/usr/lib/python2.7/dist-packages/funcsigs/__init__.py", line 712, in _bind
raise TypeError('too many positional arguments')
TypeError: too many positional arguments
>>>
>>> # Should fail
>>> print mock_a.x
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/lib/python2.7/dist-packages/mock/mock.py", line 716, in __getattr__
raise AttributeError("Mock object has no attribute %r" % name)
AttributeError: Mock object has no attribute 'x'
>>>
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