monkeypatch
是pytest中的一个非常棒的工具,允许替换当前测试范围内的任何函数。最伟大的事情之一是,即使是构造函数也可以修补。然而,不幸的是,我在修补析构函数时遇到了麻烦。它似乎只有在测试成功时才起作用。如果测试失败,将调用常规构造函数。考虑这个代码:
class MyClass:
def __init__(self):
print("Constructing MyClass")
def __del__(self):
print("Destroying MyClass")
def test_NoPatch():
c = MyClass()
def test_Patch(monkeypatch, mocker):
monkeypatch.setattr(MyClass, '__init__', mocker.MagicMock(return_value=None))
monkeypatch.setattr(MyClass, '__del__', mocker.MagicMock(return_value=None))
c = MyClass()
def test_PatchWithFailure(monkeypatch, mocker):
monkeypatch.setattr(MyClass, '__init__', mocker.MagicMock(return_value=None))
monkeypatch.setattr(MyClass, '__del__', mocker.MagicMock(return_value=None))
c = MyClass()
assert False
将给出以下结果:
====================================================================================================== test session starts ======================================================================================================
platform linux -- Python 3.8.5, pytest-6.2.2, py-1.10.0, pluggy-0.13.1 -- /home/julian/devel/tests/test_pytest_monkeypatch/testenv/bin/python3
cachedir: .pytest_cache
rootdir: /home/julian/devel/tests/test_pytest_monkeypatch
plugins: mock-3.5.1
collected 3 items
test.py::test_NoPatch Constructing MyClass
Destroying MyClass
PASSED
test.py::test_Patch PASSED
test.py::test_PatchWithFailure FAILED
=========================================================================================================== FAILURES ============================================================================================================
_____________________________________________________________________________________________________ test_PatchWithFailure _____________________________________________________________________________________________________
monkeypatch = <_pytest.monkeypatch.MonkeyPatch object at 0x7f7e94e03490>, mocker = <pytest_mock.plugin.MockerFixture object at 0x7f7e94e222b0>
def test_PatchWithFailure(monkeypatch, mocker):
monkeypatch.setattr(MyClass, '__init__', mocker.MagicMock(return_value=None))
monkeypatch.setattr(MyClass, '__del__', mocker.MagicMock(return_value=None))
c = MyClass()
> assert False
E assert False
test.py:19: AssertionError
==================================================================================================== short test summary info ====================================================================================================
FAILED test.py::test_PatchWithFailure - assert False
================================================================================================== 1 failed, 2 passed in 0.03s ==================================================================================================
Destroying MyClass
第一个没有补丁的测试按预期打印消息。正如预期的那样,第二次测试是无声的。在第三个测试中,来自构造函数的消息被抑制,来自析构函数的消息被打印
这是一个bug还是一个特性?我如何解决这个问题
有两件事会影响对
__del__
的模拟:一旦测试函数结束,方法的修补就会恢复
这在monkeypatch API reference中提到,也可以从} 方法:
monkeypatch
夹具本身的代码中看到,它在这里调用^{如this other answer中所述,调用
__del__
的时间(即,当对象被销毁和垃圾收集时)不是您可以保证或期望在测试函数引发AssertionError
时发生的事情。只有在没有更多引用时才会调用它:考虑到这两件事,发生的情况是
__del__
的monkeypatching在MyClass
对象c
最终被删除之前(当调用其__del__
函数时)被撤消或恢复。由于我们在这里处理一个异常,很可能对异常周围的局部变量的引用仍然存储在某处,因此c
实例的引用计数在其__del__
仍然被修补时没有变为零我试图通过使用
full-trace
和showlocals
选项运行测试来验证这一点。您将看到,在一个名为_multicall
的函数中,运行测试函数并捕获异常:据我所知,函数是在
hook_impl.function
中调用的(请参见传递的args
),然后发生assert False
,它被except
块捕获,异常信息存储在excinfo
中。然后,此异常信息将对c
实例的引用存储在其traceback object
中我不确定我上面所做的是否正确,但我认为:
c
对象的引用,防止在函数结束时对其进行__del__
-edc
仍然没有被垃圾收集c
实例的引用,使其能够__del__
)c
被__del__
-ed,但是monkeypatch已经不存在了现在
与其依赖于最终删除对象的时间和
monkeypatch
-ing__del__
,解决方法是将MyClass
子类化,然后完全覆盖/替换__init__
和__del__
:见Overriding destructors without calling their parents。由于派生类不调用父类“
__del__
,因此在测试期间不会调用它。它类似于monkeypatching,用其他东西替换该方法,但在这里__del__
的定义在整个测试期间仍然是模拟的。MyClass
的所有其他功能应该仍然可以从MockedMyClass
使用/测试在这里,我们看到销毁
c
只调用模拟的__del__
(实际上在这里什么都不做)。没有更多的“破坏我的类”,希望能解决你的问题。创建一个提供MockedMyClass
实例的fixture应该很简单Python不保证为解释器退出时仍然存在的对象调用__del__()方法,并且还有一些其他含义,这些含义在官方文档中有更好的解释:
https://docs.python.org/3/reference/datamodel.html
例如,如果要确保调用此方法,必须确保在解释器开始关闭之前对对象进行垃圾收集,例如使用pytest fixture等
相关问题 更多 >
编程相关推荐