为什么补丁库的行为会根据导入值的方式而改变



mock库中的patch函数对导入的方式很敏感。是否有一个深刻的原因,为什么我不能只是使用完全限定的名称,其中函数是最初定义的,不管它是如何导入到其他模块?

使用"module import"工作正常

patch_example.py:

# WORKS!
from mock import patch
import inner
def outer(x):
    return ("outer", inner.inner(x))
@patch("inner.inner")
def test(mock_inner):
    mock_inner.return_value = "MOCK"
    assert outer(1) == ("outer", "MOCK")
    return "SUCCESS"
if __name__ == "__main__":
    print test()

inner.py:

def inner(x):
    return ("inner.inner", x)

运行python patch_example.py只输出success.

但是,更改导入可能会产生非常严重的后果

使用模块别名仍然有效

# WORKS!
from mock import patch
import inner as inner2
def outer(x):
    return ("outer", inner2.inner(x))
@patch("inner.inner")
def test(mock_inner):
    mock_inner.return_value = "MOCK"
    assert outer(1) == ("outer", "MOCK")
    return "SUCCESS"
if __name__ == "__main__":
    print test()
但是,

直接导入符号需要更改完全限定名。

直接导入,inner.inner为全限定名。

# FAILS!
from mock import patch
from inner import inner
def outer(x):
    return ("outer", inner(x))
@patch("inner.inner")
def test(mock_inner):
    mock_inner.return_value = "MOCK"
    assert outer(1) == ("outer", "MOCK")
    return "SUCCESS"
if __name__ == "__main__":
    print test()

生产

% python patch_example.py
Traceback (most recent call last):
  File "patch_example.py", line 14, in <module>
    print test()
  File "/usr/local/lib/python2.7/site-packages/mock/mock.py", line 1305, in patched
    return func(*args, **keywargs)
  File "patch_example.py", line 10, in test
    assert outer(1) == ("outer", "MOCK")
AssertionError

如果我将完全限定路径更新为patch_example.inner,补丁仍然失败。

# FAILS!
from mock import patch
from inner import inner
def outer(x):
    return ("outer", inner(x))
@patch("patch_example.inner")
def test(mock_inner):
    mock_inner.return_value = "MOCK"
    assert outer(1) == ("outer", "MOCK")
    return "SUCCESS"
if __name__ == "__main__":
    print test()
% python patch_example.py
Traceback (most recent call last):
  File "patch_example.py", line 14, in <module>
    print test()
  File "/usr/local/lib/python2.7/site-packages/mock/mock.py", line 1305, in patched
    return func(*args, **keywargs)
  File "patch_example.py", line 10, in test
    assert outer(1) == ("outer", "MOCK")
AssertionError

使用__main__.inner作为我的完全限定名是正确的。

# WORKS!
from mock import patch
from inner import inner
def outer(x):
    return ("outer", inner(x))
@patch("__main__.inner")
def test(mock_inner):
    mock_inner.return_value = "MOCK"
    assert outer(1) == ("outer", "MOCK")
    return "SUCCESS"
if __name__ == "__main__":
    print test()

打印"成功"

那么,为什么我不能在将inner的值导入from inner import inner时使用原始符号inner.inner的完全限定名或使用python主模块的名称而不是__name__来修补它呢?

在OS x上测试Python 2.7.12

问题是,一旦您直接导入符号,在__main__模块中使用的绑定和inner模块中发现的绑定之间没有任何链接。所以patch 模块不会改变已经导入的符号。

使用别名导入模块并不重要,因为patch将查找sys.modules字典,后者仍然跟踪原始名称,所以这就是为什么它可以工作(实际上:在调用mock时,模块是新导入的,因此在调用patch时导入它的名称无关紧要)

换句话说:你必须修补两个绑定,因为它们实际上是不相关的。补丁没有办法知道所有对inner.inner的引用在哪里结束并修补它们。

在这种情况下,patch的第二个参数可能用于指定一个现有的模拟对象,该对象可以共享以修补所有绑定。

相关内容

  • 没有找到相关文章

最新更新