mock.patch 忽略完全导入的函数的原因是什么?

What is the reason that mock.patch ignores a fully imported function?

今天我意识到 unittest.mock.patch 如何导入函数很重要。根据使用的方式,mock.patch 调用有效或被忽略。在 Python 中,我们通常导入一个函数:

如果我使用 import osmock.patch 就像一个魅力,但它 如果我修补 from os import system,则会被忽略。

示例 1:使用导入

import os
from unittest import mock


def echo():
    os.system('echo "Hello"')


with mock.patch('os.system') as mocked:
    print(mocked)
    mocked.side_effect = Exception('Patch works!')
    echo()

示例 1 的输出

<MagicMock name='system' id='140037358656760'>

Traceback (most recent call last):
  File "/.../config/scratches/scratch_7.py", line 12, in <module>
    echo()
  File "/.../config/scratches/scratch_7.py", line 6, in echo
    os.system('echo "Hello"')
  File "/.../python3.5/unittest/mock.py", line 917, in __call__
    return _mock_self._mock_call(*args, **kwargs)
  File "/.../python3.5/unittest/mock.py", line 973, in _mock_call
    raise effect
Exception: Patch works!

示例 2:使用全功能导入和自导入

当我完全导入 os.system 时,mock.patch 会忽略 mocked.side_effect

from os import system
from unittest import mock


def echo():
    system('echo "Hello"')


with mock.patch('os.system') as mocked:
    print(mocked)
    mocked.side_effect = Exception('Patching does not work!')
    echo()

    print('Patch was ignored!')

示例 2 的输出

<MagicMock name='system' id='139851175427376'>
Hello
Patch was ignored!

在这两种情况下,我都没有收到错误,并且 mock 可以找到 os.system 作为有效路径。但是,在第二种情况下,该功能未正确修补。

当你执行 from os import system 时,你会得到一个名为 system 的变量指向 os.system 函数。稍后,您通过修补 分配 一个不同的函数给 os.system,但 system 继续指向旧函数。这与以下工作的原因相同:

tmp = a
a = b
b = tmp

在第一个示例中没有发生,因为您在模拟之前引用了 os.system。要修复您的第二个示例,我将使用以下内容:

from os import system
from unittest import mock

def echo():
    system('echo "Hello"')

with mock.patch('__main__.system') as mocked:
    print(mocked)
    mocked.side_effect = Exception('Patching does not work!')
    echo()

    print('Patch was ignored!')

这样您就可以确保修补正确的参考。这是一种相当常见的模式。如果 echo 函数位于名为 echo.py 的文件中,补丁调用将类似于 with mock.patch('echo.system').