简体   繁体   English

即使设置了side_effect之后,@ mock.patch也不会引发属性错误

[英]@mock.patch isn't raising an attribute error even after setting side_effect

I'm attempting to fix a bug in the python package caniusepython3 which arises because distlib isn't parsing pypi projects correctly. 我正在尝试修复python软件包caniusepython3中的一个错误,该错误是由于distlib无法正确解析pypi项目而出现的。 I've written this unit test 我已经写了这个单元测试

 @mock.patch('distlib.locators.locate')
 def test_blocking_dependencies_locators_fails(self, distlib_mock):
     """
     Testing the work around for //bitbucket.org/pypa/distlib/issue/59/
     """
     py3 = {'py3_project': ''}
     breaking_project = 'test_project'
     distlib_mock.locators.locate.return_value = "foo"
     distlib_mock.locators.locate.side_effect = AttributeError()
     got = dependencies.blocking_dependencies([breaking_project], py3)
     # If you'd like to test that a message is logged we can use 
     # testfixtures.LogCapture or stdout redirects.

So that when distlib fixes the error in the next release of distlib the test case will still be valid. 这样,当distlib修复了下一个distlib版本中的错误时,测试用例仍然有效。

The problem is that the MagicMock never raises a AttributeError as I expected and instead returns a string representation of the magic mock object 问题在于,MagicMock不会像我期望的那样引发AttributeError,而是返回魔术模拟对象的字符串表示形式

try:
    # sets dist to <MagicMock name='locate()' id='4447530792'>
    dist = distlib.locators.locate(project)
except AttributeError:
    # This is a work around //bitbucket.org/pypa/distlib/issue/59/
    log.warning('{0} found but had to be skipped.'.format(project))
    continue

And causes this stack trace later on because it returns the object repr, 并稍后导致此堆栈跟踪,因为它返回了对象repr,

======================================================================
ERROR: Testing the work around for //bitbucket.org/pypa/distlib/issue/59/
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/usr/local/Cellar/python3/3.4.2_1/Frameworks/Python.framework/Versions/3.4/lib/python3.4/unittest/mock.py", line 1136, in patched
    return func(*args, **keywargs)
  File "/Users/alexlord/git/caniusepython3/caniusepython3/test/test_dependencies.py", line 81, in test_blocking_dependencies_locators_fails
    got = dependencies.blocking_dependencies([breaking_project], py3)
  File "/Users/alexlord/git/caniusepython3/caniusepython3/dependencies.py", line 119, in blocking_dependencies
    return reasons_to_paths(reasons)
  File "/Users/alexlord/git/caniusepython3/caniusepython3/dependencies.py", line 43, in reasons_to_paths
    parent = reasons[blocker]
  File "/Users/alexlord/git/caniusepython3/caniusepython3/dependencies.py", line 29, in __getitem__
    return super(LowerDict, self).__getitem__(key.lower())
nose.proxy.KeyError: <MagicMock name='locate().name.lower().lower()' id='4345929400'>
-------------------- >> begin captured logging << --------------------
ciu: INFO: Checking top-level project: test_project ...
ciu: INFO: Locating <MagicMock name='locate().name.lower()' id='4344734944'>
ciu: INFO: Dependencies of <MagicMock name='locate().name.lower()' id='4344734944'>: []
--------------------- >> end captured logging << ---------------------

Why is the MagicMock not returning an exception when distlib.locator.locate() is called? 为什么在调用distlib.locator.locate()时MagicMock不返回异常?

Update: I was able to get this unit test to work when I switched to using 更新:当我切换到使用时,我能够使此单元测试正常工作

def test_blocking_dependencies_locators_fails(self):
    """
    Testing the work around for //bitbucket.org/pypa/distlib/issue/59/
    """
    with mock.patch.object(distlib.locators, 'locate') as locate_mock:
        py3 = {'py3_project': ''}
        breaking_project = 'test_project'
        locate_mock.side_effect = AttributeError()
        got = dependencies.blocking_dependencies([breaking_project], py3)
        # If you'd like to test that a message is logged we can use 
        # testfixtures.LogCapture or stdout redirects.

But I'm still wondering what I did wrong with the decorator format. 但是我仍然想知道装饰器格式做错了什么。

When you use @mock.patch , it mocks what you tell it, and passes that mock object as a parameter. 当您使用@mock.patch ,它将模拟您所告诉的内容,并将该模拟对象作为参数传递。 Thus, your distlib_mock parameter is the mock locate function. 因此,您的distlib_mock参数模拟locate功能。 You're effectively setting attributes on distlib.locators.locate.locators.locate . 您正在distlib.locators.locate.locators.locate上有效地设置属性。 Set the attributes directly on the provided mock, and things should work better. 直接在提供的模拟中设置属性,事情应该会更好。

@mock.patch('distlib.locators.locate')
def test_blocking_dependencies_locators_fails(self, locate_mock):
    # ...
    locate_mock.return_value = "foo"
    locate_mock.side_effect = AttributeError()
    # ...

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

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