为什么修补程序库的行为会根据值的导入方式而发生变化?

时间:2016-08-11 17:32:20

标签: python magicmock

patch库中的mock函数对导入内容的方式很敏感。我是否有充分理由不使用最初定义函数的完全限定名称,而不管它是如何在其他模块中导入的?

使用"模块导入"工作正常

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只会输出成功。

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

使用模块别名仍然有效

# 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()

打印&#34; SUCCESS&#34;

那么,为什么我可以使用原始符号from inner import inner的完全限定名称或使用名称来修补导入为inner.inner的内部值主python模块而不是__name__

在OS X上使用Python 2.7.12进行测试。

1 个答案:

答案 0 :(得分:1)

问题是,一旦您直接导入该符号,您在__main__模块中使用的绑定与inner中找到的绑定之间就会存在无任何链接模块。因此patch 模块 会更改已导入的符号。

使用别名导入模块并不重要,因为patch会查找仍然跟踪原始名称的sys.modules字典,这就是为什么这样做的原因(实际上:在调用mock时,模块是刚刚导入的,所以在调用patch时导入它的名称并不重要。)

换句话说:你必须修补两个绑定,因为它们实际上是无关的。修补程序无法知道对inner.inner的所有引用的最终位置并对其进行修补。

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