Pytest - 如何断言一个函数是否调用了 monkeypatched 方法
Pytest - How to assert whether a function have called monkeypatched methods
我有一个调用许多其他第 3 方方法的复杂函数。我猴子一个一个地修补了它们:
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 方法。
郑重声明,我的模拟方法没有使用任何内置模拟库资源。它们只是重新定义的方法(智能存根)。
有什么方法可以断言吗?
因此专门提供了 pytest monkeypatching
fixture,因此您可以更改一些全局属性,如环境变量、第三方库中的内容等,为您的测试提供一些可控且简单的行为。
另一方面,模拟对象旨在提供对对象的各种跟踪和检查。
两者齐头并进:您使用修补程序将某些第三方函数替换为 Mock 对象,然后执行您的代码,然后询问 Mock 对象是否确实已使用正确的参数调用它,因为正确的次数。
请注意,即使 mock
模块是 unittest
的一部分,它也可以与 pytest
完美配合。
现在至于补丁本身,这取决于你的个人喜好,并且有点取决于你想要补丁的具体内容,使用 unittest.mock.patch
更紧凑还是 pytest 的 monkeypatch
fixture。
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)
...
关于此类测试的注意事项:不要过火!它很容易导致所谓的 brittle 测试:只要对代码进行最细微的更改,测试就会中断。它可以使重构成为不可能的噩梦。
在以消息为中心的面向对象方法中使用这些类型的断言是最好的。如果被测class或方法的整个点是以特定方式调用另一个对象的方法或class,则Mock away。另一方面,如果对第三方函数的调用只是达到目的的一种手段,那么在测试中进行更高级别的测试并测试所需的 行为。
我有一个调用许多其他第 3 方方法的复杂函数。我猴子一个一个地修补了它们:
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 方法。
郑重声明,我的模拟方法没有使用任何内置模拟库资源。它们只是重新定义的方法(智能存根)。
有什么方法可以断言吗?
因此专门提供了 pytest monkeypatching
fixture,因此您可以更改一些全局属性,如环境变量、第三方库中的内容等,为您的测试提供一些可控且简单的行为。
另一方面,模拟对象旨在提供对对象的各种跟踪和检查。
两者齐头并进:您使用修补程序将某些第三方函数替换为 Mock 对象,然后执行您的代码,然后询问 Mock 对象是否确实已使用正确的参数调用它,因为正确的次数。
请注意,即使 mock
模块是 unittest
的一部分,它也可以与 pytest
完美配合。
现在至于补丁本身,这取决于你的个人喜好,并且有点取决于你想要补丁的具体内容,使用 unittest.mock.patch
更紧凑还是 pytest 的 monkeypatch
fixture。
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)
...
关于此类测试的注意事项:不要过火!它很容易导致所谓的 brittle 测试:只要对代码进行最细微的更改,测试就会中断。它可以使重构成为不可能的噩梦。
在以消息为中心的面向对象方法中使用这些类型的断言是最好的。如果被测class或方法的整个点是以特定方式调用另一个对象的方法或class,则Mock away。另一方面,如果对第三方函数的调用只是达到目的的一种手段,那么在测试中进行更高级别的测试并测试所需的 行为。