最近研究了下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的问题。