John McGehee
John McGehee

Reputation: 10299

How to mock a class hierarchy

I want to mock a hierarchy of classes. For example:

class Base(object):
    def one(self):
        return 'one'
    def two(self):
        return 'two'

class Derived(Base):
    def three(self):
        return 'three'

It is straightforward to define basic mocks:

from mock import Mock

def BaseMock():
    mock = Mock()
    mock.one.return_value = 'uno'
    mock.two.return_value = 'dos'
    return mock

def DerivedMock():
    mock = BaseMock()
    mock.three.return_value = 'tres'
    return mock

The above works, but it is incomplete. I want to use the Mock arguments spec and name. I can specify them as usual in BaseMock, but in DerivedMock, I have to modify private Mock attributes, which is bad form.

Is there a proper way to mock a hierarchy of classes complete with spec and name?

Upvotes: 3

Views: 516

Answers (1)

John McGehee
John McGehee

Reputation: 10299

To mock a class hierarchy, define the base class mock as usual:

from mock import Mock

def BaseMock():
    mock = Mock(spec=Base, name='BaseMock')
    mock.one.return_value = 'uno'
    mock.two.return_value = 'dos'
    return mock

In the derived mock, instantiate the base mock and (odious as it is) modify the Mock private attributes related spec and name:

def DerivedMock():
    mock = BaseMock()

    # Set Mock private attributes to Derived
    mock._mock_methods.extend(dir(Derived))
    mock._mock_name = 'DerivedMock'
    mock._spec_class = Derived

    mock.three.return_value = 'tres'

    # Defining DerivedMock.four would cause a spec error
    # mock.four.return_value = 'quatro'

    return mock

Now mock DerivedMock looks like class Derived, except that the return values are different:

d = DerivedMock()
assert isinstance(d, Derived)
assert repr(d).startswith("<Mock name='DerivedeMock' spec='Derived'")

assert d.one() == 'uno'
assert d.two() == 'dos'
assert d.three() == 'tres'

Upvotes: 1

Related Questions