python - Pytest:使用没有副作用的函数设置模拟
问题描述
我有一个与这个问题的答案相关的问题:
我喜欢使用通过参数接收模拟对象的函数的想法。这样,mock 的设置就可以重用了。我从答案中得出结论,模拟对象在 Python 中是可变的,并且在函数内部更改它们将产生它们在外部更改的副作用。但是,我认为产生副作用是危险的。所以,我建议如下:
def test(self, mock1):
mock1 = setup_mock1_to_always_xxx(mock1)
...
和
def setup_mock1_to_always_xxx(mock1):
# create a copy to avoid side effects
mock1 = mock1.copy() # how to copy a Mock?
mock1.return_value = "my return value"
return mock1
这可能吗?
解决方案
我建议使用pytest
固定装置而不是手动模拟复制来注入模拟。功能范围的固定装置(默认固定装置)为每个测试重新评估。示例:假设您有一个模块
# mod.py
def spam():
return eggs()
def eggs():
return "eggs"
和一个测试
from unittest.mock import patch
from mod import spam
@patch("mod.eggs")
def test_bacon(mock1):
mock1.return_value = "bacon"
assert spam() == "bacon"
现在您想将其重构为针对bacon
and进行测试bacon with eggs
。移出夹具内的补丁:
import pytest
from unittest.mock import patch
from mod import spam
@pytest.fixture
def eggs_mock():
with patch("mod.eggs") as mock1:
yield mock1
def test_bacon(eggs_mock):
eggs_mock.return_value = "bacon"
assert spam() == "bacon"
def test_bacon_with_eggs(eggs_mock):
eggs_mock.return_value = "bacon with eggs"
assert spam() == "bacon with eggs"
您现在有两个不同的mod.eggs
函数模拟,每个测试中都有一个唯一的模拟。
unittest
风格的测试
这种方法也适用于unittest
测试类,尽管注入有点冗长,因为unittest.TestCase
s 不接受测试方法中的参数。这与我的这个答案中描述的方法相同。在下面的示例中,我通过使用附加的autouse 固定装置eggs_mock
将固定装置返回值存储在实例属性中:Tests
from unittest import TestCase
from unittest.mock import patch
import pytest
from mod import spam
@pytest.fixture
def eggs_mock():
with patch("mod.eggs") as mock1:
yield mock1
class Tests(TestCase):
@pytest.fixture(autouse=True)
def inject_eggs_mock(self, eggs_mock):
self._eggs_mock = eggs_mock
def test_bacon(self):
self._eggs_mock.return_value = "bacon"
assert spam() == "bacon"
def test_bacon_with_eggs(self):
self._eggs_mock.return_value = "bacon with eggs"
assert spam() == "bacon with eggs"
推荐阅读
- vb.net - 为什么我从这个 If 语句中得到这个 NullObjectReference 异常?
- spring-boot - Wildfly 上 jboss 应用程序中的共享数据源
- asp.net-mvc - Angular 6 模板中的 ASP.NET MVC RenderAction
- javascript - 如何在 Node JS 中使用 Google Analytic API 获取会话的详细会话信息?
- android - 如何检测错误发生的原因
- swift - Xcode:如果前一个失败则跳过测试
- android - CalendarPickerView 中仅显示当前月份
- angularjs - AngularJS - 引导弹出窗口,如何在 ng-repeat 中正确使用“popover-is-open”
- python - 如何在 Python 中更改嵌套字典中键的值
- c++ - 在 OpenCV 中设置像素值(奇怪的结果)