简体   繁体   English

MagicMock 的 reset_mock 未正确重置子模拟的 side_effect

[英]MagicMock's reset_mock not properly resetting sub-mock's side_effect

I have a long-lived patch on a class, whose made instance undergoes multiple batches of assertions.我在 class 上有一个长期存在的补丁,它的实例经历了多批断言。 Please see the below code snippet for the scenario.请参阅下面的代码片段以了解该场景。

It exposes (what I think is annoying) behavior in MagicMock.reset_mock where it seemingly creates a new MagicMock inside a sub- MagicMock :它暴露了 MagicMock.reset_mock 中的(我认为很烦人的)行为,它似乎在子MagicMock.reset_mock MagicMock MagicMock

from unittest.mock import MagicMock

mock_cls = MagicMock()
mock_cls.return_value.method.side_effect = [5]
instance = mock_cls()

# Batch 1 of usage: uses side_effect
assert instance.method() == 5

mock_cls.reset_mock(return_value=True, side_effect=True)
# After this, mock_cls.return_value.method has a new id

# Batch 2 of usage: uses return_value
instance.return_value.method.return_value = 6
assert instance.method() == 6  # StopIteration

When run with Python 3.10.2, it raises a StopIteration :当使用 Python 3.10.2 运行时,它会引发StopIteration

Traceback (most recent call last):
  File "/path/to/code/play/quick_play.py", line 9, in <module>
    assert instance.method() == 6
  File "/path/to/.pyenv/versions/3.10.2/lib/python3.10/unittest/mock.py", line 1104, in __call__
    return self._mock_call(*args, **kwargs)
  File "/path/to/.pyenv/versions/3.10.2/lib/python3.10/unittest/mock.py", line 1108, in _mock_call
    return self._execute_mock_call(*args, **kwargs)
  File "/path/to/.pyenv/versions/3.10.2/lib/python3.10/unittest/mock.py", line 1165, in _execute_mock_call
    result = next(effect)
StopIteration

Is it possible to use reset_mock without creating new MagicMock ?是否可以在不创建新MagicMock reset_mock

Alternately, how can I manually reset the side_effect so the snippet runs?或者,如何手动重置side_effect以便片段运行?


Aside在旁边

What is the visited=None argument for in reset_mock 's signature? reset_mock的签名中的visited=None参数是什么? It's undocumented in the 3.10 docs , and here它在3.10 文档中没有记录,在这里

def reset_mock(self,  visited=None,*, return_value=False, side_effect=False):

Sometimes in life, you answer your own question.有时在生活中,您会回答自己的问题。

Alternate to reset_mock替代reset_mock

From the side_effect docs来自side_effect 文档

If the function returns DEFAULT then the mock will return its normal value (from the return_value ).如果 function 返回DEFAULT则模拟将返回其正常值(来自return_value )。

from unittest.mock import MagicMock, DEFAULT

mock_cls = MagicMock()
mock_cls.return_value.method.side_effect = [5]
instance = mock_cls()
instance.method()

# Works, aligns with docs
mock_cls.return_value.method.side_effect = lambda: DEFAULT
instance.method()  # <MagicMock name='mock().method()' ...>

# Alternately, this works too
mock_cls.return_value.method.side_effect = None
instance.method()  # <MagicMock name='mock().method()' ...>

You can also find this used here and mentioned here .您还可以在此处找到使用的和在此处提及的


Fixing My Snippet修复我的片段

There are two learnings here:这里有两个学习:

  • Ditching reset_mock in favor of setting side_effect = None放弃reset_mock以支持设置side_effect = None
  • return_value for the 2nd batch was used incorrectly第二批的return_value使用不正确
  • Alternately, I could've called reset_mock on the made instance或者,我可以在制作的instance上调用reset_mock
from unittest.mock import MagicMock

mock_cls = MagicMock()
mock_cls.return_value.method.side_effect = [5]
instance = mock_cls()

# Batch 1 of usage: uses side_effect
assert instance.method() == 5

mock_cls.return_value.method.side_effect = None
# instance.reset_mock(side_effect=True)  # Also works

# Batch 2 of usage: uses return_value
instance.method.return_value = 6  # Correct
# instance.return_value.method.return_value = 6  # Original incorrect
assert instance.method() == 6

Future readers: stay hungry, stay foolish.未来的读者:保持饥饿,保持愚蠢。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM