1

Python's unittest.mock allows me to patch a function such that it returns multiple values:

m = Mock(side_effect=["myName", 100, 200])

Calling m() multiple times will then return "myName", 100 and finally 200.

I can also patch a dict with patch.dict to return a mocked value, but what I am after is:

with DictMock(d, return_values=(('a',1), ('a', 2))) as d:
     assert d['a'] == 1
     assert d['a'] == 2

I have created a class that does that:

In [19]: d_
Out[19]: {'a': 1, 'b': 1}

In [20]: class MockDict:
    ...:     
    ...:     def __init__(self, in_dict, values=(), clear=False):
    ...:         self.in_dict = in_dict
    ...:         keys, values = zip(*values)
    ...:         self.keys = list(keys)
    ...:         self.values = list(values)
    ...:     
    ...:     def __enter__(self):
    ...:         self.orig_dict = self.in_dict.copy()
    ...:         return self
    ...: 
    ...:     def __exit__(self, *args):
    ...:         
    ...:         return False
    ...:     
    ...:     def __getitem__(self, key):
    ...:         try:
    ...:             idx = self.keys.index(key)
    ...:             self.keys.pop(idx)
    ...:             val = self.values.pop(idx)
    ...:             return val
    ...:         except ValueError:
    ...:             raise KeyError(key)
    ...:         

In [21]: with MockDict(d_, values=(("a",4),("a",5))) as d:
    ...:     print(d["a"])
    ...:     print(d["a"])
    ...:     
4
5

I know my class is limited in a way that is does not copy the original key value pairs ... this can be improved of course.

Can you suggest a better way to achieve this?

update: explaining my motivation

I need to test the following snippet:

    code = self.instance.state['Code']
    while code != status:
        time.sleep(3)
        self.instance.reload()
        code = self.instance.state['Code']

Hence, I have to call instance.state['Code'] twice, to go through the function code and reach self.instance.reload()

oz123
  • 27,559
  • 27
  • 125
  • 187
  • I'm guessing that you're not patching a regular dictionary (because `__getitem__` for a regular dict doesn't have side-effects). Why don't you just patch the class that has this weird behavior? – mgilson Sep 05 '17 at 12:47
  • mgilson, I am really trying to patch this snippet `code = self.instance.state['Code']`, and I simply could not think of a better way... – oz123 Sep 05 '17 at 12:49
  • I agree with @mgilson that this behaviour looks very un-dict-like. – NPE Sep 05 '17 at 12:51
  • But you can patch that code using `mock.patch.dict`, no? – mgilson Sep 05 '17 at 12:51
  • nope, patch.dict, allows returning just one value per key. This means that can't call it twice and get two different values ... – oz123 Sep 05 '17 at 12:53
  • @NPE, see my update as for why I am doing this ... maybe there is a better way ... – oz123 Sep 05 '17 at 12:56
  • Sounds to me like you want to mock the `__getitem__` method on `self.instance.state`, no? Alternatively, you could change the `__eq__` method on the object you store in `self.instance.state['Code']`, but that's a bit smelly. Obviously what you really want is for `self.instance.reload` to change the value at "Code" in the mocked dict, why not just do that? – DylanYoung Nov 16 '20 at 22:16
  • Yeah, I guess I could have simply mock `self.instance.reload`. This is so long ago, and in the meanwhile I got the hangs of mock. I would probably not post this question today. It's worth leaving because the MockDict implementation I guess. – oz123 Nov 16 '20 at 22:46

0 Answers0