我正在使用
unittest.mock.patch
创建一个测试,看起来像这样:
class TestService:
def test_patched(self):
service = Service()
with patch.object(service, "_send_to_third_party") as patch_send:
with patch.object(service, "_notify_listeners") as patch_notify:
service.do_something()
patch_send.assert_called_once()
patch_notify.assert_called_once()
所有这些都工作正常,但我想将这些补丁放入一个方法中,以便我可以在一堆测试中重用它。像这样:
class TestService:
def _patched_service(self):
service = Service()
with patch.object(service, "_send_to_third_party") as patch_send:
with patch.object(service, "_notify_listeners") as patch_notify:
return service, patch_send, patch_notify
def test_patched(self):
service, patch_send, patch_notify = self._patched_service()
service.do_something()
patch_send.assert_called_once()
patch_notify.assert_called_once()
这会失败,因为它调用的是 real 方法而不是修补过的方法。
为了简化,在下面的测试中,第三个选项失败了,我试图弄清楚为什么以及是否有一个好方法来做我想做的事?
from unittest.mock import patch
class ExampleClass:
def __init__(self):
self.value = 0
def add(self, value):
self.value += self._add(value)
def subtract(self, value):
self.value -= self._subtract(value)
def what_is_my_value(self):
return self.value
def _add(self, value):
return value
def _subtract(self, value):
return value
def patch_me(exa: ExampleClass):
with patch.object(exa, '_add', return_value=99):
with patch.object(exa, '_subtract', return_value=66):
return exa
class TestPatchingWorks:
def test_unpatched_works(self):
exa = ExampleClass()
exa.add(5)
exa.subtract(2)
assert exa.what_is_my_value() == 3
def test_patching_works_with_patch_class(self):
exa = ExampleClass()
with patch.object(ExampleClass, '_add', return_value=30):
with patch.object(ExampleClass, '_subtract', return_value=10):
assert exa.what_is_my_value() == 0
exa.add(5)
assert exa.what_is_my_value() == 30
exa.subtract(2)
assert exa.what_is_my_value() == 20
def test_patching_works_with_patch_instance(self):
exa = ExampleClass()
with patch.object(exa, '_add', return_value=40):
with patch.object(exa, '_subtract', return_value=30):
assert exa.what_is_my_value() == 0
exa.add(5)
assert exa.what_is_my_value() == 40
exa.subtract(2)
assert exa.what_is_my_value() == 10
def test_patching_works_with_function(self):
exa = ExampleClass()
exa = patch_me(exa)
assert exa.what_is_my_value() == 0
exa.add(5)
assert exa.what_is_my_value() == 99
exa.subtract(2)
assert exa.what_is_my_value() == 33
嗯,一旦你退出
with
块,补丁就会解开。
@contextlib.contextmanager
def _patched_service(self):
service = Service()
with patch.object(service, "_send_to_third_party") as patch_send:
with patch.object(service, "_notify_listeners") as patch_notify:
yield service, patch_send, patch_notify
...
def test_patched(self):
with self._patched_service() as (service, patch_send, patch_notify):
service.do_something()
patch_send.assert_called_once()
patch_notify.assert_called_once()
就可以了。
在 Pytest 固定装置中也有同样的事情
@pytest.fixture
def patched_service():
service = Service()
with patch.object(service, "_send_to_third_party") as patch_send:
with patch.object(service, "_notify_listeners") as patch_notify:
yield service, patch_send, patch_notify
def test_patched(patched_service):
(service, patch_send, patch_notify) = patched_service
service.do_something()
patch_send.assert_called_once()
patch_notify.assert_called_once()