Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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 {}

like image 301
Daniel Paczuski Bak Avatar asked Mar 01 '19 18:03

Daniel Paczuski Bak


1 Answers

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.

like image 125
Austin R. Scott Avatar answered Nov 04 '22 00:11

Austin R. Scott