给出以下代码:
import os
from unittest.mock import patch
def sys_exit_new1():
print("sys_exit_new1:", os.environ.get("BANANA"))
def sys_exit_new2():
print("sys_exit_new2:", os.environ.get("BANANA"))
@patch("sys.exit", new_callable=sys_exit_new1)
@patch.dict(os.environ, {"BANANA": "1"})
@patch.dict(os.environ, {"BANANA": "2"})
@patch("sys.exit", new_callable=sys_exit_new2)
@patch.dict(os.environ, {"BANANA": "3"})
def test_mytest(m1, m2):
...
test_mytest()
测试将产生:
sys_exit_new2: 2
sys_exit_new1: 2
有人可以解释一下为什么吗? 文档说:
请注意,装饰器是从底部向上应用的。这是Python应用装饰器的标准方式。传递到测试函数中的创建的模拟的顺序与此顺序匹配。
如果这是真的,我希望它输出:
sys_exit_new2: 3
sys_exit_new2: 1
所以
patch.dict
的行为有所不同。
unittest.mock.patch
做了一些奇怪的事情,大多数装饰器,包括unittest.mock.patch.dict
,都不会这样做。
第一次用
unittest.mock.patch
装饰函数时,它会生成一个修补程序包装器,并在包装器上设置一个 patchings
属性,其中包含要修补的内容列表。如果您再次应用 unittest.mock.patch
,它只会 更新 patchings
列表,而不是生成新的包装器。
所以,当你装饰你的函数时,这些事情会按顺序发生:
@patch.dict(os.environ, {"BANANA": "3"})
生成一个包装器,将 os.environ['BANANA']
修补到 "3"
并调用原始函数。
@patch("sys.exit", new_callable=sys_exit_new2)
生成具有 patchings
属性的包装器。该包装器应用 patchings
列表中指定要应用的任何补丁,然后调用 BANANA-3 包装器。
@patch.dict(os.environ, {"BANANA": "2"})
生成一个包装器,将 os.environ['BANANA']
修补到 "2"
并调用 patch
包装器。
@patch.dict(os.environ, {"BANANA": "1"})
生成一个包装器,将 os.environ['BANANA']
修补到 "1"
并调用 BANANA-2 包装器。
@patch("sys.exit", new_callable=sys_exit_new1)
检测到 BANANA-1 包装器已具有 patchings
列表(继承自 BANANA-2 包装器,而 BANANA-2 包装器又继承自原始 patch
包装器)。它不会生成新的包装器,而是更新 patchings
列表并返回 BANANA-1 包装器。
所以最后,
test_mytest
被设置为BANANA-1包装器,它调用BANANA-2包装器,它调用patch
包装器,它调用BANANA-3包装器,它调用原始函数。
当您调用
test_mytest
时,您正在调用 BANANA-1 包装器。这调用了 BANANA-2 包装器,它又调用了 patch
包装器。在调用 patch
包装器时,os.environ['BANANA']
设置为 "2"
。此时, patch
包装器应用 patchings
列表中显示要应用的两个补丁,因此它调用您的 sys_exit_new1
和 sys_exit_new2
函数。由于此时 os.environ['BANANA']
设置为 "2"
,这就是您的两个函数打印的内容。