Mock patching for 'from/import' statement in Python
坑
最近研究了下Python的mock库,实际用的时候碰到了这样的坑:
我有三个py文件分别放了测试代码,被测试的代码以及被测试代码中调用的方法:
mylib.py
:
class Foo(object):
def print_foo(self):
return 'foo'
myfunc.py
:
from mylib import Foo
def some_function():
instance = Foo()
return instance.print_foo()
test_myfunc.py
:
from myfunc import some_function
def test_some_function():
# Will create a MagicMock object to replace class `Foo`
with patch("mylib.Foo") as MockFoo:
mock_instance = MockFoo.return_value
mock_instance.print_foo.return_value = 'bar'
result = some_function()
assert result == 'bar'
然而实际运行时,some_function
中的Foo
类仍然是mylib.Foo
对象,而不是我生成的MagicMock
对象,也就是说mock在这里并没有起到替换被测代码中对象的作用。
Why
原因其实和Python的import机制有关(之前也碰到过类似的问题):
通过
from/import
来import的对象其实是在此模块中创建了一个import对象的引用来指向这个对象
所以上述代码中myfunc.py
中的Foo
其实在全局空间是myfunc.Foo
,而test_myfunc.py
中我是对mylib.Foo
进行了patch,所以实际看上去并没有替换被测代码中的Foo
对象。
Solution
正确地打好patch即可:
from myfunc import some_function
def test_some_function():
# Will create a MagicMock object to replace class `Foo`
with patch("myfunc.Foo") as MockFoo:
mock_instance = MockFoo.return_value
mock_instance.print_foo.return_value = 'bar'
result = some_function()
assert result == 'bar'
Conclusion
使用mock的patch时一定要注意where to patch的问题。