mock.patch 作用域

在使用 unittest.mock.patch 前一定要读一下文档,你百度出来的一定是最简单的例子也没有说明 patch 作用条件,只是访官网写的最简单例子,如果你刚接触 python unittest 可能会有很大一个坑在等着你

unittest 官网文档的关于 patcher 的第一句话很重要:

The patch decorators are used for patching objects only within the scope of the function they decorate. They automatically handle the unpatching for you, even if exceptions are raised. All of these functions can also be used in with statements or as class decorators.

stackoverflow 有个回答更容易理解一些:

patch works by (temporarily) changing the object that a name points to with another one. There can be many names pointing to any individual object, so for patching to work you must ensure that you patch the name used by the system under test.

The basic principle is that you patch where an object is looked up, which is not necessarily the same place as where it is defined.

有时候认真看文档也是一种水平,我开始忽略了第一句话,白白浪费了很多时间。下面我用例子来说明一下 patch 作用域:

假设我在 lib 模块下边有个类 Foo


class Foo:

    def run(self):
        return 1

然后我在 api.bar 调用 Foo().run()

# api.py 文件

from lib import Foo

def bar():
    instance = Foo()
    instance.run()

下边我们来写 bar 的测试用例:

from unittest.mock import patch
from api import bar

@patch('lib.Foo')
def test_bar1(mock_foo):
    """"
    这是个反例,这里 mock 并没有起到真正的作用
    """
    instance = mock_foo.return_value
    instance.run.return_value = 2
    print(bar().run()) # return 1
    assert bar().run() == 2 # False

@patch('api.Foo')
def test_bar2(mock_foo):
    instance = mock_foo.return_value
    instance.run.return_value = 2
    print(bar().run()) # return 2
    assert bar().run() == 2 # true
    assert bar() is instance

注意 test_bar1 mock 并没有起到我们所预期的作用, Foo().run() 依旧返回 1, 而 test_bar2 是我们所预期的。

test_bar1test_bar2 的区别在于 test_bar1 patch 的目标是 lib.Footest_bar2 patch 的目标是 api.Foo

test_bar1test_bar2 就唯一的局别就是:@patch('lib.Foo') <==> @patch('api.Foo') ,可以看出 patch target 参数是在运行处对目标进行一个替换,而不是替换源类


螃蟹在晨跑
255 声望4 粉丝

Make it work, make it fast, make it best~!


下一篇 »
ansible 笔记