Shy*_*der 7 python unit-testing
我模块的结构:
foo:
- load() # from DB
bar:
- check() # with user
- take_action()
Run Code Online (Sandbox Code Playgroud)
我想通过模拟加载和检查来测试take_action(它在执行操作之前基本上加载值并检查用户).
这是嘲笑:
mock_load = Mock(side_effects=[<>, <>, <>]) # different data sets
mock_check = Mock(return_value=True) # User approval
Run Code Online (Sandbox Code Playgroud)
我如何使用patch.multiple来实现这一目标?
with patch.multiple(??):
# proceed to test
take_action
Run Code Online (Sandbox Code Playgroud)
Mic*_*ico 16
简短的回答是你不能patch.multiple()用来做.如patch.multiple中所述,所有参数都将应用于所有创建的模拟,并且所有参数必须是同一对象的属性.您必须通过单个补丁调用来执行此操作.
不幸的是,你正在使用Python 2.6,所以你可以只使用nested氟里昂contextlib像指出蟒蛇:创建几个方面管理者"与"块和多重背景下`with`在Python 2.6的语句.
也许更清洁,更简单的方法是@patch用作装饰器:
@patch("foo.load",side_effects=["a","b","c"])
@patch("bar.check",return_value=True)
def test_mytest(mock_check,mock_load):
take_action()
assert mock_load.called
assert mock_check.called
Run Code Online (Sandbox Code Playgroud)
如果您在测试类的所有测试中都需要它,您可以装饰该类并在所有测试方法中使用模拟:
@patch("foo.load",side_effects=["a","b","c"])
@patch("bar.check",return_value=True)
class TestMyTest(unittest.TestCase)
def test_mytestA(self,mock_check,mock_load):
take_action()
self.assertTrue(mock_load.called)
self.assertTrue(mock_check.called)
def test_mytestA(self,mock_check,mock_load):
mock_check.return_value = False
take_action()
self.assertTrue(mock_load.called)
self.assertTrue(mock_check.called)
Run Code Online (Sandbox Code Playgroud)
最后,你可以用它做的with和contextlib与第一例子变成:
from contextlib import nested
with nested(patch("foo.load",side_effects=["a","b","c"]), patch("bar.check",return_value=True)) as (mock_load, mock_check):
take_action()
assert mock_load.called
assert mock_check.called
Run Code Online (Sandbox Code Playgroud)
......或者手工筑巢......
with patch("foo.load",side_effects=["a","b","c"]) as mock_load:
with patch("bar.check",return_value=True)) as mock_check:
take_action()
assert mock_load.called
assert mock_check.called
Run Code Online (Sandbox Code Playgroud)
我觉得装饰器是最易读和易用的.
从 Python 3.10 开始,您可以使用带括号的上下文管理器来实现此目的,如下所示:
from unittest.mock import patch
def test_something():
with (
patch("foo.load", side_effect=["a","b","c"]), # don't have to have `as`
patch("bar.check", return_value=True) as mock_check, # example using `as`
):
# proceed to test
Run Code Online (Sandbox Code Playgroud)
您也可以手动嵌套。如果仍然使用End-Of-Lifed Python 2.6 或 2.7,您可以使用嵌套,如python 3 文档或python 2 文档中所示:
with patch("foo.load", side_effect=["a","b","c"]) as mock_load:
with patch("bar.check",return_value=True)) as mock_check:
# proceed to test
# or you can use comma separated like version that has this structure
#
# with A() as a, B() as b:
# ...
#
# which, if we applied it, would easily become a long one liner like so
with patch("bar.check",return_value=True)) as mock_check, patch("foo.load", side_effect=["a","b","c"]) as mock_load:
# proceed to test
Run Code Online (Sandbox Code Playgroud)
在这些环境中,我通常选择通过这样做来保持清晰度:
def cron():
with patch(
"foo.load", side_effect=["a","b","c"]
) as mock_load, patch(
"bar.check", return_value=True
) as mock_check, patch(
"spam.Publish", return_value=42
) as mock_publish:
# proceed to test
Run Code Online (Sandbox Code Playgroud)