python unittest: mocking a dict-like object

I am attempting to do the following:

def test_fn(self):
    cool_dict = {}
    blah = Mock(spec=DictField, wraps=cool_dict)
    blah['key'] = 'val'
    print(cool_dict))
    return False

Basically, I want to ensure that anything which happens to blah is allowed for a DictField, but I want anything that happens to blah to actually happen to cool_dict, so I can see assert that it has a certain state.

How can I do this? The above code fails:

FAILED (errors=1)

Error
Traceback (most recent call last):
  File "C:\Program Files\Python 3.5\Lib\unittest\case.py", line 59, in testPartExecutor
    yield
  File "C:\Program Files\Python 3.5\Lib\unittest\case.py", line 605, in run
    testMethod()
  File "C:\Users\danie01.AD\PycharmProjects\component\component\Default\Functional\pam_team_management\test\test_team_create.py", line 23, in test_populate_info_page
    blah['key'] = 'val'
TypeError: 'Mock' object does not support item assignment

I also tried it with a MagicMock:

def test_populate_info_page(self):
    cool_dict = {}
    blah = MagicMock(spec=DictField, wraps=cool_dict)
    blah['key'] = 'val'
    print(cool_dict)
    return False

Which didn't fail, but cool_dict was still {}


Solution 1:

In Python, you can also make use of magic methods which are the built-in methods on classes. For dict objects, you would want to override the __getitem__() and __setitem__() magic methods.

As an example, when you make the statement blah['key'] = 'var', it's actually calling blah.__setitem__('key', 'var'). So you'll want to mock those two magic methods, and then check on the status of those mocked methods.

Here's an example of how I might try what you're talking about:

>>> cool_dict = {'key': 'val'}
>>> blah = Mock()
>>> blah.__getitem__ = Mock()
>>> blah.__getitem__.side_effect = cool_dict.__getitem__
>>> blah['key']
'val'
>>> blah.__getitem__.assert_called() # No assertion raised, which means that it was called
>>> 

So in this example, the __getitem__() method of the 'blah' object is the thing you're going to be using a Mock() to mock, and then you create a side effect: it triggers the same __getitem__() function on the cool_dict. After it's been called, you can inspect that Mock afterward to see whether it was called and what it was called with. See Mocking Magic Methods for more context.