我有一个复杂的函数,可以调用许多其他 3rd 方方法。我一一把它们修补出来:
import ThirdParty as tp
def my_method():
tp.func_3rd_party_1()
...
tp.func_3rd_party_5()
return "some_value"
在我的测试中:
import pytest
def test_my_method(monkeypatch):
monkeypatch.setattr(ThirdParty, 'func_3rd_party_1', some_mock_1())
...
monkeypatch.setattr(ThirdParty, 'func_3rd_party_5', some_mock_5())
return_value = my_method()
assert return value
这运行得很好,但这种形式的测试对我来说太含蓄了。我想明确指出,monkeypatched 方法确实被调用了。
为了记录,我的模拟方法没有使用任何内置的模拟库资源。它们只是重新定义的方法(智能存根)。
有什么办法可以断言吗?
答案 0 :(得分:1)
因此专门提供了 pytest monkeypatching
固定装置,以便您可以更改一些全局属性,例如环境变量、第三方库中的内容等,从而为您的测试提供一些可控且简单的行为。
另一方面,Mock 对象旨在提供对对象的各种跟踪和检查。
这两者是相辅相成的:您使用补丁将某些第三方函数替换为 Mock 对象,然后执行您的代码,然后询问 Mock 对象是否确实使用正确的参数调用了正确的编号多次。
请注意,尽管 mock
模块是 unittest
的一部分,但它与 pytest
一起工作得非常好。
现在至于修补本身,这取决于您的个人喜好,并且在一定程度上取决于您想要修补的具体内容,是使用 unittest.mock.patch
更紧凑还是使用 pytest 的 monkeypatch
固定装置。
import pytest
from unittest.mock import Mock
def test_my_method(monkeypatch):
# refer to the mock module documentation for more complex
# set ups, where the mock object _also_ exhibits some behavior.
# as is, calling the function doesn't actually _do_ anything.
some_mock_1 = Mock()
...
some_mock_5 = Mock(return_value=66)
monkeypatch.setattr(ThirdParty, 'func_3rd_party_1', some_mock_1)
...
monkeypatch.setattr(ThirdParty, 'func_3rd_party_5', some_mock_5)
some_mock_1.assert_called_once()
some_mock_5.assert_called_with(42)
...
现在对此类测试进行说明:不要过火!它很容易导致所谓的脆弱测试:对您的代码稍加改动就会中断的测试。它可以使重构成为不可能的噩梦。
当您在以消息为中心的面向对象方法中使用这些类型的断言时,它们是最好的。如果被测类或方法的整个点是以特定方式调用另一个对象的方法或类,则 Mock 离开。另一方面,如果对第三方函数的调用只是达到目的的一种手段,那么请在更高的级别进行测试并测试所需的行为。