Search code examples
pythonpython-mockabc

How to use abc abstract base class as mock spec?


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?


Solution

  • 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'
    >>>